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_INIT
Function, called before any other function, responsible for initialization of platform specific state.
LSP_ARCH_CREATE_MEM
Function responsible for allocating memory that will represent
lsp_mem_t
structure (described in following chapters) and it’s initialization.
LSP_ARCH_FREE_MEM
Function responsible for freeing previously allocated memory.
LSP_ARCH_CREATE_IN_STREAM
Function responsible for allocating memory that will represent
lsp_in_stream_t
structure (described in following chapters) and it’s initialization.
LSP_ARCH_FREE_IN_STREAM
Function responsible for freeing previously allocated memory.
LSP_ARCH_CREATE_OUT_STREAM
Function responsible for allocating memory that will represent
lsp_out_stream_t
structure (described in following chapters) and it’s initialization.
LSP_ARCH_FREE_OUT_STREAM
Function 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); }