Tuesday, September 22, 2015

Calling C From Bank 1

One of my goals is to be able to separately compile a program that runs in banks 1 and 2 while the system manages from banks 0 and 3. Since I don't want to have duplicates of the c library, I need a way to let a the compiler use the c library and driver functions that are in bank 0. So this morning, I coded up a system to do that.

I added a crt1.s which is the crt start up for bank 1. It doesn't need to deal with interrupts, and some basic start up code, for example. It just needs to prepare the stack pointer, deal with it's C initialization, and then start main. At the end of main, it has to return to the caller in bank 0.

I haven't tested any of this, but the basic plan is to have a vector of jumps at the top of bank 0. A version will sit at 0x3f11. The vector is jumps to functions in bank 0.

For example. If I want to call printf from the compiled code for bank 1, it will link the vector which will have a:
_printf::
 jp 0
compiled into a location somewhere in the 3f11-3ffd range. Specifically at 0x4000 - sizeof(struct c_vector) + offsetof(struct c_vector, printf).

Because of the way the intel hex programmer works, this will not be burnt into the flash because it is outside the 0x4000-0x7fff range. So a call to printf will go to that location where the jp 0 is. But there won't be a jp 0 there. What will be there is a jp _printf placed there by the compiler that compiled the bank 0 code. The code compiled for bank 1 will, as a result, call the printf compiled separately for bank 0. Of course, the struct c_vector and the jp's have to align perfectly.

If that description was a little complicated, have a look at the code (recall it's not tested yet). It's in progress.

cvector.h:

#ifndef INCLUDE_CVECTORS_H
#define INCLUDE_CVECTORS_H

#ifndef _SDCC_MALLOC_TYPE_MLH
#define _SDCC_MALLOC_TYPE_MLH
#endif

#ifndef __SDCC_BROKEN_STRING_FUNCTIONS
#define __SDCC_BROKEN_STRING_FUNCTIONS
#endif

#ifndef __SDCC_z80
#define __SDCC_z80
#endif

#ifndef _Bool
#define _Bool unsigned char
#endif

#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include "ringBuffer.h"


struct c_vector
{
    // c_ext
    uint8_t jp4e;
    void (*gets_length)(int maxlen);
    uint8_t jp4d;
    void(*gets_echoSuppress)(bool suppressEcho);

    // idle
    uint8_t jp4c;
    void(*idle)(void(*pIdleFn)(void* p), void* p);

    // interrupt
    uint8_t jp4b;
    void(*di)();
    uint8_t jp4a;
    void(*ei)();

    // gpio
    uint8_t jp49;
    uint8_t(*gpioReadDirection)();
    uint8_t jp48;
    void(*gpioWriteDirection)(uint8_t direction, uint8_t mask);
    uint8_t jp447;
    uint8_t(*gpioReadOutLevel)();
    uint8_t jp46;
    void(*gpioWriteLevel)(uint8_t out, uint8_t mask);
    uint8_t jp45;
    void(*gpioWriteBitLevel)(uint8_t bit, bool level);
    uint8_t jp44;
    uint8_t(*gpioReadInLevel)();

    // spi
    uint8_t jp43;
    uint8_t(*spiExchange)(uint8_t b, uint8_t flags, uint8_t count);

    // tick
    uint8_t jp42;
    uint32_t(*getTicks)();

    // timer
    uint8_t jp41;
    void(*timerInit)();
    uint8_t jp40;
    int(*timerGetAvailableTimerId)();
    uint8_t jp3f;
    void(*timerFree)(int timerId);
    uint8_t jp3e;
    void(*timerSet)(int timerId, uint32_t interval, bool repeat, bool start, void(*cb)(int timer));
    uint8_t jp3d;
    void(*timerGet)(int timerId, uint32_t* pTrigger, uint32_t* pInterval, bool* pRepeat, bool* pRunning, void(**pCb)(int timer));
    uint8_t jp3c;
    void(*timerStop)(int timerId);
    uint8_t jp3b;
    void(*timerRun)(int timerId);
    uint8_t jp3a;
    void(*timerResetInterval)(int timerId);

    // uart
    uint8_t jp39;
    bool(*uartConfig)(uint8_t configLow);
    uint8_t jp38;
    void(*uartSupressRTS)(bool always, bool exceptRecv);
    uint8_t jp37;
    uint8_t(*uartErrorReadAndClear)(uint8_t clearMask);
    uint8_t jp36;
    RB_INT_TYPE(*send)(uint8_t* pByte, RB_INT_TYPE count);
    uint8_t jp35;
    RB_INT_TYPE(*recv)(uint8_t* pByte, RB_INT_TYPE count);
    uint8_t jp34;
    void(*pend)(void(*pIdleFn)(void* p), void* p);

    // stdio
    uint8_t jp33;
    char(*getchar)();
    uint8_t jp32;
    void(*putchar)(char c);
    uint8_t jp31;
    int(*puts)(const char* s);
    uint8_t jp30;
    char* (*gets)(char* s);
    uint8_t jp2f;
    int(*printf)(char* format, ...);
    uint8_t jp2e;
    int(*vprintf)(char* format, va_list ap);
    uint8_t jp2d;
    int(*sprintf)(char* dest, char* format, ...);
    uint8_t jp2c;
    int(*vsprintf)(char* dest, char* format, va_list ap);

    // stdlib
    uint8_t jp2b;
    void* (*malloc)(size_t size);
    uint8_t jp2a;
    void* (*calloc)(size_t size, size_t count);
    uint8_t jp29;
    void* (*realloc)(void* old, size_t newSize);
    uint8_t jp28;
    void(*free)(void* m);


    // string
    uint8_t jp27;
    void* (*memcpy)(void* dest, const void* src, size_t n);
    uint8_t jp26;
    void* (*memmove)(void *dest, const void *src, size_t n);
    uint8_t jp25;
    char* (*strcpy)(char* dest, const char* src);
    uint8_t jp24;
    char* (*strncpy)(char* dest, const char* src, size_t n);
    uint8_t jp23;
    char* (*strcat)(char* dest, const char* src);
    uint8_t jp22;
    char* (*strncat)(char* dest, const char* src, size_t n);
    uint8_t jp21;
    int(*memcmp)(const void* s1, const void* s2, size_t n);
    uint8_t jp20;
    int(*strcmp)(const char* s1, const char* s2);
    uint8_t jp1f;
    int(*strncmp)(const char* s1, const char* s2, size_t n);
    uint8_t jp1e;
    size_t(*strxfrm)(char *dest, const char* src, size_t n);
    uint8_t jp1d;
    void* (*memchr)(const void *s, int c, size_t n);
    uint8_t jp1c;
    char* (*strchr)(const char *s, char c); /* c should be int according to standard. */
    uint8_t jp1b;
    size_t(*strcspn)(const char *s, const char *reject);
    uint8_t jp1a;
    char* (*strpbrk)(const char *s, const char *accept);
    uint8_t jp19;
    char* (*strrchr)(const char *s, char c); /* c should be int according to standard. */
    uint8_t jp18;
    size_t(*strspn)(const char *s, const char *accept);
    uint8_t jp17;
    char* (*strstr)(const char* haystack, const char *needle);
    uint8_t jp16;
    char* (*strtok)(char* str, const char * delim);
    uint8_t jp15;
    void* (*memset)(void *s, unsigned char c, size_t n); /* c should be int according to standard. */
    uint8_t jp14;
    size_t(*strlen)(const char *s);

    // stdlib
    uint8_t jp13;
    int(*atoi)(const char* s);
    uint8_t jp12;
    long(*atol)(const char* s);
    uint8_t jp11;
    int(*rand)();
    uint8_t jp10;
    void(*srand)(unsigned int seed);
    uint8_t jp0f;
    int(*abs)(int i);
    uint8_t jp0e;
    long(*labs)(long i);

    // ctype
    uint8_t jp0d;
    int(*isblank)(int c);
    uint8_t jp0c;
    int(*isdigit)(int c);
    uint8_t jp0b;
    int(*islower)(int c);
    uint8_t jp0a;
    int(*isupper)(int c);
    uint8_t jp09;
    int(*isalnum)(int c);
    uint8_t jp08;
    int(*isalpha)(int c);
    uint8_t jp07;
    int(*iscntrl)(int c);
    uint8_t jp06;
    int(*isgraph)(int c);
    uint8_t jp05;
    int(*isprint)(int c);
    uint8_t jp04;
    int(*ispunct)(int c);
    uint8_t jp03;
    int(*isspace)(int c);
    uint8_t jp02;
    int(*isxdigit)(int c);
    uint8_t jp01;
    int(*tolower)(int c);
    uint8_t jp00;
    int(*toupper)(int c);

    // version --corresponds to 0x3ffe
    uint16_t _cVectorVersion;
};

#endif



c_vector.c:

#include "c_vector.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <malloc.h>
#include "menuDriver.h"
#include "c_ext.h"
#include "interrupt.h"
#include "gpio.h"
#include "spi.h"
#include "tick.h"
#include "idle.h"
#include "timer.h"
#include "uart.h"
#include "diag.h"

#ifndef __SDCC
#define __at(x)
#endif

void* proxy_memcpy(void* dest, const void* src, size_t n)
{
    return memcpy(dest, src, n);
}

void* proxy_memmove(void *dest, const void *src, size_t n)
{
    return memmove(dest, src, n);
}

char* proxy_strcpy(char* dest, const char* src)
{
    return strcpy(dest, src);
}

char* proxy_strncpy(char* dest, const char* src, size_t n)
{
    return strncpy(dest, src, n);

}

char* proxy_strchr(const char *s, char c)
{
    return strchr(s, c);
}

void* proxy_memset(void *s, unsigned char c, size_t n)
{
    return memset(s, c, n);
}
#define JP_OPCODE (uint8_t)(0xc3)

const struct c_vector __at(0x4000 - sizeof(struct c_vector)) C_VECTOR = {
 JP_OPCODE,
    gets_length,
    JP_OPCODE,
    gets_echoSuppress,

    JP_OPCODE,
    idle,

    JP_OPCODE,
    di,
    JP_OPCODE,
    ei,

    JP_OPCODE,
    gpioReadDirection,
    JP_OPCODE,
    gpioWriteDirection,
    JP_OPCODE,
    gpioReadOutLevel,
    JP_OPCODE,
    gpioWriteLevel,
    JP_OPCODE,
    gpioWriteBitLevel,
    JP_OPCODE,
    gpioReadInLevel,

    JP_OPCODE,
    spiExchange,

    JP_OPCODE,
    getTicks,

    JP_OPCODE,
    timerInit,
    JP_OPCODE,
    timerGetAvailableTimerId,
    JP_OPCODE,
    timerFree,
    JP_OPCODE,
    timerSet,
    JP_OPCODE,
    timerGet,
    JP_OPCODE,
    timerStop,
    JP_OPCODE,
    timerRun,
    JP_OPCODE,
    timerResetInterval,

    JP_OPCODE,
    uartConfig,
    JP_OPCODE,
    uartSupressRTS,
    JP_OPCODE,
    uartErrorReadAndClear,
    JP_OPCODE,
    send,
    JP_OPCODE,
    recv,
    JP_OPCODE,
    pend,

    JP_OPCODE,
    getchar,
    JP_OPCODE,
    putchar,
    JP_OPCODE,
    puts,
    JP_OPCODE,
    gets,
    JP_OPCODE,
    printf,
    JP_OPCODE,
    vprintf,
    JP_OPCODE,
    sprintf,
    JP_OPCODE,
    vsprintf,

    JP_OPCODE,
    malloc,
    JP_OPCODE,
    calloc,
    JP_OPCODE,
    realloc,
    JP_OPCODE,
    free,

    JP_OPCODE,
    proxy_memcpy,
    JP_OPCODE,
    proxy_memmove,
    JP_OPCODE,
    proxy_strcpy,
    JP_OPCODE,
    proxy_strncpy,
    JP_OPCODE,
    strcat,
    JP_OPCODE,
    strncat,
    JP_OPCODE,
    memcmp,
    JP_OPCODE,
    strcmp,
    JP_OPCODE,
    strncmp,
    JP_OPCODE,
    strxfrm,
    JP_OPCODE,
    memchr,
    JP_OPCODE,
    proxy_strchr,
    JP_OPCODE,
    strcspn,
    JP_OPCODE,
    strpbrk,
    JP_OPCODE,
    strrchr,
    JP_OPCODE,
    strspn,
    JP_OPCODE,
    strstr,
    JP_OPCODE,
    strtok,
    JP_OPCODE,
    proxy_memset,
    JP_OPCODE,
    strlen,

    JP_OPCODE,
    atoi,
    JP_OPCODE,
    atol,
    JP_OPCODE,
    rand,
    JP_OPCODE,
    srand,
    JP_OPCODE,
    abs,
    JP_OPCODE,
    labs,

    JP_OPCODE,
    isblank,
    JP_OPCODE,
    isdigit,
    JP_OPCODE,
    islower,
    JP_OPCODE,
    isupper,
    JP_OPCODE,
    isalnum,
    JP_OPCODE,
    isalpha,
    JP_OPCODE,
    iscntrl,
    JP_OPCODE,
    isgraph,
    JP_OPCODE,
    isprint,
    JP_OPCODE,
    ispunct,
    JP_OPCODE,
    isspace,
    JP_OPCODE,
    isxdigit,
    JP_OPCODE,
    tolower,
    JP_OPCODE,
    toupper,

    0x0101
};

const struct MenuItem menuItems[] =
{
 // Banking

 // Flash

 // Running

    {
 NULL,
 NULL
    }
};

void main()
{
    ShowByte(0x00);
    ShowByte(0xff);

    diagInit();
    DiagDir(1);
    tickInit();
    uartInit();
    gpioWriteLevel(0x00, 0x08);
    gpioWriteDirection(0x08, 0x08);
    uartSupressRTS(false, true);

    while (!uartConfig(0 | (uint8_t)UART_BAUD_9600))
    {
        // do nothing
    }

    ei();

    runMenu();
}

crt1.s:

;;

 .module crt1

 .globl _main

 .area _HEADER (ABS)
; This next section must remain in sync with cvectors.h
; This will be part of the intelhex file but will be ignored by the intel hex programmer
; because it is out of the range

; inspect crt1.rel and ensure _cVectorVersion looks like this:
; S _cVectorVersion Def3FFE
; if it is not, adjust this .org accordingly and update _cVectorVersion to reflect a new C vector
 .org 0x3f11
_gets_length::
 jp 0
_gets_echoSuppress::
 jp 0
_idle::
 jp 0
_di::
 jp 0
_e::
 jp 0
_gpioReadDirection::
 jp 0
_gpioWriteDirection::
 jp 0
_gpioReadOutLevel::
 jp 0
_gpioWriteLevel::
 jp 0
_gpioWriteBitLevel::
 jp 0
_gpioReadInLevel::
 jp 0
_spiExchange::
 jp 0
_getTicks::
 jp 0
_timerInit::
 jp 0
_timerGetAvailableTimerId::
 jp 0
_timerFree::
 jp 0
_timerSet::
 jp 0
_timerGet::
 jp 0
_timerStop::
 jp 0
_timerRun::
 jp 0
_timerResetInterval::
 jp 0
_uartConfig::
 jp 0
_uartSupressRTS::
 jp 0
_uartErrorReadAndClear::
 jp 0
_send::
 jp 0
_recv::
 jp 0
_pend::
 jp 0
_getchar::
 jp 0
_putchar::
 jp 0
_puts::
 jp 0
_gets::
 jp 0
_printf::
 jp 0
_vprintf::
 jp 0
_sprintf::
 jp 0
_vsprintf::
 jp 0
_malloc::
 jp 0
_calloc::
 jp 0
_realloc::
 jp 0
_free::
 jp 0
_memcpy::
 jp 0
_memmove::
 jp 0
_strcpy::
 jp 0
_strncpy::
 jp 0
_strcat::
 jp 0
_strncat::
 jp 0
_memcmp::
 jp 0
_strcmp::
 jp 0
_strncmp::
 jp 0
_strxfrm::
 jp 0
_memchr::
 jp 0
_strchr::
 jp 0
_strcspn::
 jp 0
_strpbrk::
 jp 0
_strrchr::
 jp 0
_strspn::
 jp 0
_strstr::
 jp 0
_strtok::
 jp 0
_memset::
 jp 0
_strlen::
 jp 0
_atoi::
 jp 0
_atol::
 jp 0
_rand::
 jp 0
_randSeed::
 jp 0
_abs::
 jp 0
_labs::
 jp 0
_isblank::
 jp 0
_isdigit::
 jp 0
_islower::
 jp 0
_isupper::
 jp 0
_isalnum::
 jp 0
_isalpha::
 jp 0
_iscntrl::
 jp 0
_isgraph::
 jp 0
_isprint::
 jp 0
_ispunct::
 jp 0
_isspace::
 jp 0
_isxdigit::
 jp 0
_tolower::
 jp 0
_toupper::
 jp 0
_cVectorVersion::
 .dw 0x0101

 ; 4000-4020 unused for now since intel Hex might overlap 4000

 ;; required c vector version the applet expects
 ;; C program must provide a const uint16_t  CVECTOR_VERSION = 0x0101; // adjust accordingly
 .globl _CVECTOR_VERSION

 .org 0x4020
 .dw _CVECTOR_VERSION
 
 ;; pointer to name of the applet
 ;; C program must provide a const char* const APPLET_NAME = "applet name here";
 .globl _APPLET_NAME
 .org 0x4022
 .dw _APPLET_NAME

 ;; pointer to name of the applet
 ;; C program must provide a const char* const APPLET_NAME = "applet name here";
 .globl _APPLET_TIMESTAMP
 .org 0x4024
 .dw _APPLET_TIMESTAMP

 ;; applet indicator--is these aren't 1,2,3,4, then the flash block is not an applet
 .org 0x4026
 .dw 0x0102
 .dw 0x0304

 .org 0x4030
init:
 ;; Set stack pointer directly above top of memory.
 ld hl, #0
 add hl, sp
 ld sp,#0xc000
 ;; Create a space to be used by bank switcher
 push hl

 ;; store the SP from the code that called this
 push hl

 ;; Initialise global variables
 call gsinit

 call _main

 ; return to code that called us
 pop hl
 ld sp, hl

 ret

 ;; Ordering of segments for the linker.
 .area _HOME
 .area _CODE
 .area _INITIALIZER
 .area   _GSINIT
 .area   _GSFINAL

 .area _DATA
 .area _INITIALIZED
 .area _BSEG
 .area   _BSS
 .area   _HEAP

 .area   _CODE

 .area   _GSINIT
gsinit::
 ld bc, #l__INITIALIZER
 ld a, b
 or a, c
 jr Z, gsinit_next
 ld de, #s__INITIALIZED
 ld hl, #s__INITIALIZER
 ldir
gsinit_next:

 .area   _GSFINAL
 ret


Here is how it's used. I define APPLET_NAME, APPLET_TIMESTAMP, and CVECTOR_VERSION, and main(). Next it is liniked with a Makefile that links with crt1 instead of crt0 and that uses --code-seg and --data-seg of 0x4020 and 0x8000.

Here is an example that I'm working toward.
clock.c:

#include <stdio.h>
#include "uart.h"
#include "timer.h"

const char* const APPLET_NAME = "clock";
const char* const APPLET_TIMESTAMP = __TIMESTAMP__;
const uint16_t CVECTOR_VERSION = 0x0101;

int clockId = -1;
uint8_t hour;
uint8_t min;
uint8_t sec;

void showClock()
{
    while (sec > 59)
    {
        sec = 0;
        min++;
    }
    while (min > 59)
    {
        min = 0;
        hour++;
    }
    while (hour > 12)
    {
        hour = 1;
    }
    printf("%02hd:%02hd:%02hd\r\n", hour, min, sec);
}

void clockCB(int timerId)
{
    timerId;
    sec++;
    showClock();
}

void main()
{
    char c;
    bool go = false;

    uartSupressRTS(false, false);

    if (clockId == -1)
    {
        clockId = timerGetAvailableTimerId();
    }
    if (clockId == -1)
    {
        return;
    }

    timerSet(clockId, 1000, true, false, clockCB);

    puts("'h' for hour, 'm' for minutes 'g' to go, 's' to stop");
    clockCB(clockId);
    while (!go)
    {
        c = getchar();
        switch (c)
        {
        case 'h':
            hour++;
            break;
        case 'm':
            min++;
            break;
        case 'M':
            min += 10;
            break;
        case 'g':
            timerRun(clockId);
            go = true;
            break;
        case 's':
            timerStop(clockId);
            go = true;
            break;
        }
        showClock();
    }

}


I'll need a menu function that switches a bank 1 and 2 in and then jumps to 0x4020. Also a menu item that lists all eligible banks with bank name and c vector version.

No comments:

Post a Comment