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); }