Architecture abstraction layer#
To enable easier targeting of different execution platforms and provide decoupling from standard C library (or other dependencies), thin abstraction layer is required.
Supported target platforms#
Constant definition LSP_ARCH is used as identifier of desired target
platform. This definition should be set during C source preprocessing to
one of supported values:
LSP_ARCH_POSIX(POSIX target)
LSP_ARCH_AVR8(8bit AVR target)
Depending on value of this constant, different platform specific implementations will be included.
C data types#
arch.h defines fixed length integers (lsp_int8_t, lsp_int16_t,
lsp_int32_t, lsp_uint8_t, lsp_uint16_t, lsp_uint32_t)
independent of target platform. If target has available standard C library,
this types are aliases to types defined by stdint.h. In case target doesn’t
have standard C library available, these types should be defined by
appropriate C integer types (char, short, int, long, …).
Additionally, lsp_bool_t is defined as alias to _Bool. If _Bool
type is not available, lsp_uint8_t can be used. Care is taken not to depend
on specifics of integer to _Bool conversions so that other integer types
could be used as lsp_bool_t.
Platform specific functions#
Each platform abstraction layer implementation (arch/*.c) is responsible for functions aliased with following names:
LSP_ARCH_INITFunction, called before any other function, responsible for initialization of platform specific state.
LSP_ARCH_CREATE_MEMFunction responsible for allocating memory that will represent
lsp_mem_tstructure (described in following chapters) and it’s initialization.
LSP_ARCH_FREE_MEMFunction responsible for freeing previously allocated memory.
LSP_ARCH_CREATE_IN_STREAMFunction responsible for allocating memory that will represent
lsp_in_stream_tstructure (described in following chapters) and it’s initialization.
LSP_ARCH_FREE_IN_STREAMFunction responsible for freeing previously allocated memory.
LSP_ARCH_CREATE_OUT_STREAMFunction responsible for allocating memory that will represent
lsp_out_stream_tstructure (described in following chapters) and it’s initialization.
LSP_ARCH_FREE_OUT_STREAMFunction responsible for freeing previously allocated memory.
In case of memory constrained targets (e.g. LSP_ARCH_AVR8), functions
responsible for allocating memory can return pointers to statically
preallocated memory instead of dynamically allocated. For this target
platforms, associated freeing functions don’t implement any functionality.
Input stream#
During initialization of input stream, platform specific implementation is responsible for providing function pointer with signature:
typedef lsp_int16_t (*lsp_stream_getchar_t)(lsp_in_stream_t *s);
Provided function is responsible for reading single character from input
stream (used by REPL). In case of POSIX, it’s behavior corresponds to
getchar provided by standard C library.
Details of input stream implementation are available in following chapters.
Output stream#
During initialization of output stream, platform specific implementation is responsible for providing function pointer with signature:
typedef lsp_int16_t (*lsp_stream_putchar_t)(lsp_out_stream_t *s, lsp_int16_t v);
Provided function is responsible for writing single character to output
stream (used by REPL). In case of POSIX, it’s behavior corresponds to
putchar provided by standard C library.
Details of output stream implementation are available in following chapters.
Source code#
arch.h#
#ifndef LISP16_ARCH_H
#define LISP16_ARCH_H
#define LSP_ARCH_POSIX 0
#define LSP_ARCH_AVR8 1
#define LSP_ARCH_STM32 2
#ifndef LSP_ARCH
#error LSP_ARCH not defined
#endif
#if LSP_ARCH == LSP_ARCH_POSIX
#include <stdint.h>
typedef _Bool lsp_bool_t;
typedef int8_t lsp_int8_t;
typedef int16_t lsp_int16_t;
typedef int32_t lsp_int32_t;
typedef uint8_t lsp_uint8_t;
typedef uint16_t lsp_uint16_t;
typedef uint32_t lsp_uint32_t;
#elif LSP_ARCH == LSP_ARCH_AVR8
#include <stdint.h>
typedef _Bool lsp_bool_t;
typedef int8_t lsp_int8_t;
typedef int16_t lsp_int16_t;
typedef int32_t lsp_int32_t;
typedef uint8_t lsp_uint8_t;
typedef uint16_t lsp_uint16_t;
typedef uint32_t lsp_uint32_t;
#elif LSP_ARCH == LSP_ARCH_STM32
#include <stdint.h>
typedef _Bool lsp_bool_t;
typedef int8_t lsp_int8_t;
typedef int16_t lsp_int16_t;
typedef int32_t lsp_int32_t;
typedef uint8_t lsp_uint8_t;
typedef uint16_t lsp_uint16_t;
typedef uint32_t lsp_uint32_t;
#else
#error unknown LSP_ARCH
#endif
#ifndef NULL
#define NULL ((void *)0)
#endif
#ifndef true
#define true ((lsp_bool_t)1)
#endif
#ifndef false
#define false ((lsp_bool_t)0)
#endif
#endif
arch/avr8.h#
#ifndef LISP16_ARCH_AVR8_H
#define LISP16_ARCH_AVR8_H
#include "../mem.h"
#include "../stream.h"
#define LSP_ARCH_INIT lsp_arch_avr8_init
#define LSP_ARCH_CREATE_MEM lsp_arch_avr8_create_mem
#define LSP_ARCH_FREE_MEM lsp_arch_avr8_free_mem
#define LSP_ARCH_CREATE_IN_STREAM lsp_arch_avr8_create_in_stream
#define LSP_ARCH_FREE_IN_STREAM lsp_arch_avr8_free_in_stream
#define LSP_ARCH_CREATE_OUT_STREAM lsp_arch_avr8_create_out_stream
#define LSP_ARCH_FREE_OUT_STREAM lsp_arch_avr8_free_out_stream
void lsp_arch_avr8_init();
lsp_mem_t *lsp_arch_avr8_create_mem();
void lsp_arch_avr8_free_mem(lsp_mem_t *m);
lsp_in_stream_t *lsp_arch_avr8_create_in_stream();
void lsp_arch_avr8_free_in_stream(lsp_in_stream_t *s);
lsp_out_stream_t *lsp_arch_avr8_create_out_stream();
void lsp_arch_avr8_free_out_stream(lsp_out_stream_t *s);
#endif
arch/avr8.c#
#include "avr8.h"
#include <avr/io.h>
#define MEM_SIZE 0x01c0
#define UART_BAUD 9600
static lsp_int16_t avr8_getchar(lsp_in_stream_t *s) {
while (!(UCSR0A & (1 << 7)))
;
return UDR0;
}
static lsp_int16_t avr8_putchar(lsp_out_stream_t *s, lsp_int16_t c) {
while (!(UCSR0A & (1 << 5)))
;
UDR0 = c;
}
static lsp_uint8_t avr8_mem[sizeof(lsp_mem_t) + MEM_SIZE * sizeof(lsp_cell_t)];
static lsp_in_stream_t avr8_in_stream;
static lsp_out_stream_t avr8_out_stream;
void lsp_arch_avr8_init() {
UBRR0 = F_CPU / 16 / UART_BAUD - 1;
UCSR0B |= _BV(TXEN0) | _BV(RXEN0);
}
lsp_mem_t *lsp_arch_avr8_create_mem() {
lsp_mem_t *m = (void *)avr8_mem;
if (lsp_mem_init(m, MEM_SIZE) != LSP_SUCCESS)
return NULL;
return m;
}
void lsp_arch_avr8_free_mem(lsp_mem_t *m) {}
lsp_in_stream_t *lsp_arch_avr8_create_in_stream() {
lsp_in_stream_t *s = &avr8_in_stream;
lsp_in_stream_init(s, avr8_getchar);
return s;
}
void lsp_arch_avr8_free_in_stream(lsp_in_stream_t *s) {}
lsp_out_stream_t *lsp_arch_avr8_create_out_stream() {
lsp_out_stream_t *s = &avr8_out_stream;
lsp_out_stream_init(s, avr8_putchar);
return s;
}
void lsp_arch_avr8_free_out_stream(lsp_out_stream_t *s) {}
arch/posix.h#
#ifndef LISP16_ARCH_POSIX_H
#define LISP16_ARCH_POSIX_H
#include "../mem.h"
#include "../stream.h"
#define LSP_ARCH_INIT lsp_arch_posix_init
#define LSP_ARCH_CREATE_MEM lsp_arch_posix_create_mem
#define LSP_ARCH_FREE_MEM lsp_arch_posix_free_mem
#define LSP_ARCH_CREATE_IN_STREAM lsp_arch_posix_create_in_stream
#define LSP_ARCH_FREE_IN_STREAM lsp_arch_posix_free_in_stream
#define LSP_ARCH_CREATE_OUT_STREAM lsp_arch_posix_create_out_stream
#define LSP_ARCH_FREE_OUT_STREAM lsp_arch_posix_free_out_stream
void lsp_arch_posix_init();
lsp_mem_t *lsp_arch_posix_create_mem();
void lsp_arch_posix_free_mem(lsp_mem_t *m);
lsp_in_stream_t *lsp_arch_posix_create_in_stream();
void lsp_arch_posix_free_in_stream(lsp_in_stream_t *s);
lsp_out_stream_t *lsp_arch_posix_create_out_stream();
void lsp_arch_posix_free_out_stream(lsp_out_stream_t *s);
#endif
arch/posix.c#
#include "posix.h"
#include <stdio.h>
#include <stdlib.h>
#define MEM_SIZE 0x4000
static lsp_int16_t posix_getchar(lsp_in_stream_t *s) {
int c = getchar();
if (c == EOF)
return LSP_EOF;
return c;
}
static lsp_int16_t posix_putchar(lsp_out_stream_t *s, lsp_int16_t c) {
return putchar(c);
}
void lsp_arch_posix_init() {}
lsp_mem_t *lsp_arch_posix_create_mem() {
lsp_mem_t *m = malloc(sizeof(lsp_mem_t) + sizeof(lsp_cell_t) * MEM_SIZE);
if (lsp_mem_init(m, MEM_SIZE) != LSP_SUCCESS) {
free(m);
return NULL;
}
return m;
}
void lsp_arch_posix_free_mem(lsp_mem_t *m) { free(m); }
lsp_in_stream_t *lsp_arch_posix_create_in_stream() {
lsp_in_stream_t *s = malloc(sizeof(lsp_in_stream_t));
lsp_in_stream_init(s, posix_getchar);
return s;
}
void lsp_arch_posix_free_in_stream(lsp_in_stream_t *s) { free(s); }
lsp_out_stream_t *lsp_arch_posix_create_out_stream() {
lsp_out_stream_t *s = malloc(sizeof(lsp_out_stream_t));
lsp_out_stream_init(s, posix_putchar);
return s;
}
void lsp_arch_posix_free_out_stream(lsp_out_stream_t *s) { free(s); }