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:

  1 #ifndef INCLUDE_CVECTORS_H
  2 #define INCLUDE_CVECTORS_H
  3 
  4 #ifndef _SDCC_MALLOC_TYPE_MLH
  5 #define _SDCC_MALLOC_TYPE_MLH
  6 #endif
  7 
  8 #ifndef __SDCC_BROKEN_STRING_FUNCTIONS
  9 #define __SDCC_BROKEN_STRING_FUNCTIONS
 10 #endif
 11 
 12 #ifndef __SDCC_z80
 13 #define __SDCC_z80
 14 #endif
 15 
 16 #ifndef _Bool
 17 #define _Bool unsigned char
 18 #endif
 19 
 20 #include <stdint.h>
 21 #include <stdbool.h>
 22 #include <stdarg.h>
 23 #include "ringBuffer.h"
 24 
 25 
 26 struct c_vector
 27 {
 28     // c_ext
 29     uint8_t jp4e;
 30     void (*gets_length)(int maxlen);
 31     uint8_t jp4d;
 32     void(*gets_echoSuppress)(bool suppressEcho);
 33 
 34     // idle
 35     uint8_t jp4c;
 36     void(*idle)(void(*pIdleFn)(void* p), void* p);
 37 
 38     // interrupt
 39     uint8_t jp4b;
 40     void(*di)();
 41     uint8_t jp4a;
 42     void(*ei)();
 43 
 44     // gpio
 45     uint8_t jp49;
 46     uint8_t(*gpioReadDirection)();
 47     uint8_t jp48;
 48     void(*gpioWriteDirection)(uint8_t direction, uint8_t mask);
 49     uint8_t jp447;
 50     uint8_t(*gpioReadOutLevel)();
 51     uint8_t jp46;
 52     void(*gpioWriteLevel)(uint8_t out, uint8_t mask);
 53     uint8_t jp45;
 54     void(*gpioWriteBitLevel)(uint8_t bit, bool level);
 55     uint8_t jp44;
 56     uint8_t(*gpioReadInLevel)();
 57 
 58     // spi
 59     uint8_t jp43;
 60     uint8_t(*spiExchange)(uint8_t b, uint8_t flags, uint8_t count);
 61 
 62     // tick
 63     uint8_t jp42;
 64     uint32_t(*getTicks)();
 65 
 66     // timer
 67     uint8_t jp41;
 68     void(*timerInit)();
 69     uint8_t jp40;
 70     int(*timerGetAvailableTimerId)();
 71     uint8_t jp3f;
 72     void(*timerFree)(int timerId);
 73     uint8_t jp3e;
 74     void(*timerSet)(int timerId, uint32_t interval, bool repeat, bool start, void(*cb)(int timer));
 75     uint8_t jp3d;
 76     void(*timerGet)(int timerId, uint32_t* pTrigger, uint32_t* pInterval, bool* pRepeat, bool* pRunning, void(**pCb)(int timer));
 77     uint8_t jp3c;
 78     void(*timerStop)(int timerId);
 79     uint8_t jp3b;
 80     void(*timerRun)(int timerId);
 81     uint8_t jp3a;
 82     void(*timerResetInterval)(int timerId);
 83 
 84     // uart
 85     uint8_t jp39;
 86     bool(*uartConfig)(uint8_t configLow);
 87     uint8_t jp38;
 88     void(*uartSupressRTS)(bool always, bool exceptRecv);
 89     uint8_t jp37;
 90     uint8_t(*uartErrorReadAndClear)(uint8_t clearMask);
 91     uint8_t jp36;
 92     RB_INT_TYPE(*send)(uint8_t* pByte, RB_INT_TYPE count);
 93     uint8_t jp35;
 94     RB_INT_TYPE(*recv)(uint8_t* pByte, RB_INT_TYPE count);
 95     uint8_t jp34;
 96     void(*pend)(void(*pIdleFn)(void* p), void* p);
 97 
 98     // stdio
 99     uint8_t jp33;
100     char(*getchar)();
101     uint8_t jp32;
102     void(*putchar)(char c);
103     uint8_t jp31;
104     int(*puts)(const char* s);
105     uint8_t jp30;
106     char* (*gets)(char* s);
107     uint8_t jp2f;
108     int(*printf)(char* format, ...);
109     uint8_t jp2e;
110     int(*vprintf)(char* format, va_list ap);
111     uint8_t jp2d;
112     int(*sprintf)(char* dest, char* format, ...);
113     uint8_t jp2c;
114     int(*vsprintf)(char* dest, char* format, va_list ap);
115 
116     // stdlib
117     uint8_t jp2b;
118     void* (*malloc)(size_t size);
119     uint8_t jp2a;
120     void* (*calloc)(size_t size, size_t count);
121     uint8_t jp29;
122     void* (*realloc)(void* old, size_t newSize);
123     uint8_t jp28;
124     void(*free)(void* m);
125 
126 
127     // string
128     uint8_t jp27;
129     void* (*memcpy)(void* dest, const void* src, size_t n);
130     uint8_t jp26;
131     void* (*memmove)(void *dest, const void *src, size_t n);
132     uint8_t jp25;
133     char* (*strcpy)(char* dest, const char* src);
134     uint8_t jp24;
135     char* (*strncpy)(char* dest, const char* src, size_t n);
136     uint8_t jp23;
137     char* (*strcat)(char* dest, const char* src);
138     uint8_t jp22;
139     char* (*strncat)(char* dest, const char* src, size_t n);
140     uint8_t jp21;
141     int(*memcmp)(const void* s1, const void* s2, size_t n);
142     uint8_t jp20;
143     int(*strcmp)(const char* s1, const char* s2);
144     uint8_t jp1f;
145     int(*strncmp)(const char* s1, const char* s2, size_t n);
146     uint8_t jp1e;
147     size_t(*strxfrm)(char *dest, const char* src, size_t n);
148     uint8_t jp1d;
149     void* (*memchr)(const void *s, int c, size_t n);
150     uint8_t jp1c;
151     char* (*strchr)(const char *s, char c); /* c should be int according to standard. */
152     uint8_t jp1b;
153     size_t(*strcspn)(const char *s, const char *reject);
154     uint8_t jp1a;
155     char* (*strpbrk)(const char *s, const char *accept);
156     uint8_t jp19;
157     char* (*strrchr)(const char *s, char c); /* c should be int according to standard. */
158     uint8_t jp18;
159     size_t(*strspn)(const char *s, const char *accept);
160     uint8_t jp17;
161     char* (*strstr)(const char* haystack, const char *needle);
162     uint8_t jp16;
163     char* (*strtok)(char* str, const char * delim);
164     uint8_t jp15;
165     void* (*memset)(void *s, unsigned char c, size_t n); /* c should be int according to standard. */
166     uint8_t jp14;
167     size_t(*strlen)(const char *s);
168 
169     // stdlib
170     uint8_t jp13;
171     int(*atoi)(const char* s);
172     uint8_t jp12;
173     long(*atol)(const char* s);
174     uint8_t jp11;
175     int(*rand)();
176     uint8_t jp10;
177     void(*srand)(unsigned int seed);
178     uint8_t jp0f;
179     int(*abs)(int i);
180     uint8_t jp0e;
181     long(*labs)(long i);
182 
183     // ctype
184     uint8_t jp0d;
185     int(*isblank)(int c);
186     uint8_t jp0c;
187     int(*isdigit)(int c);
188     uint8_t jp0b;
189     int(*islower)(int c);
190     uint8_t jp0a;
191     int(*isupper)(int c);
192     uint8_t jp09;
193     int(*isalnum)(int c);
194     uint8_t jp08;
195     int(*isalpha)(int c);
196     uint8_t jp07;
197     int(*iscntrl)(int c);
198     uint8_t jp06;
199     int(*isgraph)(int c);
200     uint8_t jp05;
201     int(*isprint)(int c);
202     uint8_t jp04;
203     int(*ispunct)(int c);
204     uint8_t jp03;
205     int(*isspace)(int c);
206     uint8_t jp02;
207     int(*isxdigit)(int c);
208     uint8_t jp01;
209     int(*tolower)(int c);
210     uint8_t jp00;
211     int(*toupper)(int c);
212 
213     // version --corresponds to 0x3ffe
214     uint16_t _cVectorVersion;
215 };
216 
217 #endif

c_vector.c:

  1 #include "c_vector.h"
  2 #include <stdio.h>
  3 #include <stdlib.h>
  4 #include <string.h>
  5 #include <ctype.h>
  6 #include <malloc.h>
  7 #include "menuDriver.h"
  8 #include "c_ext.h"
  9 #include "interrupt.h"
 10 #include "gpio.h"
 11 #include "spi.h"
 12 #include "tick.h"
 13 #include "idle.h"
 14 #include "timer.h"
 15 #include "uart.h"
 16 #include "diag.h"
 17 
 18 #ifndef __SDCC
 19 #define __at(x)
 20 #endif
 21 
 22 void* proxy_memcpy(void* dest, const void* src, size_t n)
 23 {
 24     return memcpy(dest, src, n);
 25 }
 26 
 27 void* proxy_memmove(void *dest, const void *src, size_t n)
 28 {
 29     return memmove(dest, src, n);
 30 }
 31 
 32 char* proxy_strcpy(char* dest, const char* src)
 33 {
 34     return strcpy(dest, src);
 35 }
 36 
 37 char* proxy_strncpy(char* dest, const char* src, size_t n)
 38 {
 39     return strncpy(dest, src, n);
 40 
 41 }
 42 
 43 char* proxy_strchr(const char *s, char c)
 44 {
 45     return strchr(s, c);
 46 }
 47 
 48 void* proxy_memset(void *s, unsigned char c, size_t n)
 49 {
 50     return memset(s, c, n);
 51 }
 52 #define JP_OPCODE (uint8_t)(0xc3)
 53 
 54 const struct c_vector __at(0x4000 - sizeof(struct c_vector)) C_VECTOR = {
 55  JP_OPCODE,
 56     gets_length,
 57     JP_OPCODE,
 58     gets_echoSuppress,
 59 
 60     JP_OPCODE,
 61     idle,
 62 
 63     JP_OPCODE,
 64     di,
 65     JP_OPCODE,
 66     ei,
 67 
 68     JP_OPCODE,
 69     gpioReadDirection,
 70     JP_OPCODE,
 71     gpioWriteDirection,
 72     JP_OPCODE,
 73     gpioReadOutLevel,
 74     JP_OPCODE,
 75     gpioWriteLevel,
 76     JP_OPCODE,
 77     gpioWriteBitLevel,
 78     JP_OPCODE,
 79     gpioReadInLevel,
 80 
 81     JP_OPCODE,
 82     spiExchange,
 83 
 84     JP_OPCODE,
 85     getTicks,
 86 
 87     JP_OPCODE,
 88     timerInit,
 89     JP_OPCODE,
 90     timerGetAvailableTimerId,
 91     JP_OPCODE,
 92     timerFree,
 93     JP_OPCODE,
 94     timerSet,
 95     JP_OPCODE,
 96     timerGet,
 97     JP_OPCODE,
 98     timerStop,
 99     JP_OPCODE,
100     timerRun,
101     JP_OPCODE,
102     timerResetInterval,
103 
104     JP_OPCODE,
105     uartConfig,
106     JP_OPCODE,
107     uartSupressRTS,
108     JP_OPCODE,
109     uartErrorReadAndClear,
110     JP_OPCODE,
111     send,
112     JP_OPCODE,
113     recv,
114     JP_OPCODE,
115     pend,
116 
117     JP_OPCODE,
118     getchar,
119     JP_OPCODE,
120     putchar,
121     JP_OPCODE,
122     puts,
123     JP_OPCODE,
124     gets,
125     JP_OPCODE,
126     printf,
127     JP_OPCODE,
128     vprintf,
129     JP_OPCODE,
130     sprintf,
131     JP_OPCODE,
132     vsprintf,
133 
134     JP_OPCODE,
135     malloc,
136     JP_OPCODE,
137     calloc,
138     JP_OPCODE,
139     realloc,
140     JP_OPCODE,
141     free,
142 
143     JP_OPCODE,
144     proxy_memcpy,
145     JP_OPCODE,
146     proxy_memmove,
147     JP_OPCODE,
148     proxy_strcpy,
149     JP_OPCODE,
150     proxy_strncpy,
151     JP_OPCODE,
152     strcat,
153     JP_OPCODE,
154     strncat,
155     JP_OPCODE,
156     memcmp,
157     JP_OPCODE,
158     strcmp,
159     JP_OPCODE,
160     strncmp,
161     JP_OPCODE,
162     strxfrm,
163     JP_OPCODE,
164     memchr,
165     JP_OPCODE,
166     proxy_strchr,
167     JP_OPCODE,
168     strcspn,
169     JP_OPCODE,
170     strpbrk,
171     JP_OPCODE,
172     strrchr,
173     JP_OPCODE,
174     strspn,
175     JP_OPCODE,
176     strstr,
177     JP_OPCODE,
178     strtok,
179     JP_OPCODE,
180     proxy_memset,
181     JP_OPCODE,
182     strlen,
183 
184     JP_OPCODE,
185     atoi,
186     JP_OPCODE,
187     atol,
188     JP_OPCODE,
189     rand,
190     JP_OPCODE,
191     srand,
192     JP_OPCODE,
193     abs,
194     JP_OPCODE,
195     labs,
196 
197     JP_OPCODE,
198     isblank,
199     JP_OPCODE,
200     isdigit,
201     JP_OPCODE,
202     islower,
203     JP_OPCODE,
204     isupper,
205     JP_OPCODE,
206     isalnum,
207     JP_OPCODE,
208     isalpha,
209     JP_OPCODE,
210     iscntrl,
211     JP_OPCODE,
212     isgraph,
213     JP_OPCODE,
214     isprint,
215     JP_OPCODE,
216     ispunct,
217     JP_OPCODE,
218     isspace,
219     JP_OPCODE,
220     isxdigit,
221     JP_OPCODE,
222     tolower,
223     JP_OPCODE,
224     toupper,
225 
226     0x0101
227 };
228 
229 const struct MenuItem menuItems[] =
230 {
231  // Banking
232 
233  // Flash
234 
235  // Running
236 
237     {
238  NULL,
239  NULL
240     }
241 };
242 
243 void main()
244 {
245     ShowByte(0x00);
246     ShowByte(0xff);
247 
248     diagInit();
249     DiagDir(1);
250     tickInit();
251     uartInit();
252     gpioWriteLevel(0x00, 0x08);
253     gpioWriteDirection(0x08, 0x08);
254     uartSupressRTS(false, true);
255 
256     while (!uartConfig(0 | (uint8_t)UART_BAUD_9600))
257     {
258         // do nothing
259     }
260 
261     ei();
262 
263     runMenu();
264 }

crt1.s:

  1 ;;
  2 
  3  .module crt1
  4 
  5  .globl _main
  6 
  7  .area _HEADER (ABS)
  8 ; This next section must remain in sync with cvectors.h
  9 ; This will be part of the intelhex file but will be ignored by the intel hex programmer
 10 ; because it is out of the range
 11 
 12 ; inspect crt1.rel and ensure _cVectorVersion looks like this:
 13 ; S _cVectorVersion Def3FFE
 14 ; if it is not, adjust this .org accordingly and update _cVectorVersion to reflect a new C vector
 15  .org 0x3f11
 16 _gets_length::
 17  jp 0
 18 _gets_echoSuppress::
 19  jp 0
 20 _idle::
 21  jp 0
 22 _di::
 23  jp 0
 24 _e::
 25  jp 0
 26 _gpioReadDirection::
 27  jp 0
 28 _gpioWriteDirection::
 29  jp 0
 30 _gpioReadOutLevel::
 31  jp 0
 32 _gpioWriteLevel::
 33  jp 0
 34 _gpioWriteBitLevel::
 35  jp 0
 36 _gpioReadInLevel::
 37  jp 0
 38 _spiExchange::
 39  jp 0
 40 _getTicks::
 41  jp 0
 42 _timerInit::
 43  jp 0
 44 _timerGetAvailableTimerId::
 45  jp 0
 46 _timerFree::
 47  jp 0
 48 _timerSet::
 49  jp 0
 50 _timerGet::
 51  jp 0
 52 _timerStop::
 53  jp 0
 54 _timerRun::
 55  jp 0
 56 _timerResetInterval::
 57  jp 0
 58 _uartConfig::
 59  jp 0
 60 _uartSupressRTS::
 61  jp 0
 62 _uartErrorReadAndClear::
 63  jp 0
 64 _send::
 65  jp 0
 66 _recv::
 67  jp 0
 68 _pend::
 69  jp 0
 70 _getchar::
 71  jp 0
 72 _putchar::
 73  jp 0
 74 _puts::
 75  jp 0
 76 _gets::
 77  jp 0
 78 _printf::
 79  jp 0
 80 _vprintf::
 81  jp 0
 82 _sprintf::
 83  jp 0
 84 _vsprintf::
 85  jp 0
 86 _malloc::
 87  jp 0
 88 _calloc::
 89  jp 0
 90 _realloc::
 91  jp 0
 92 _free::
 93  jp 0
 94 _memcpy::
 95  jp 0
 96 _memmove::
 97  jp 0
 98 _strcpy::
 99  jp 0
100 _strncpy::
101  jp 0
102 _strcat::
103  jp 0
104 _strncat::
105  jp 0
106 _memcmp::
107  jp 0
108 _strcmp::
109  jp 0
110 _strncmp::
111  jp 0
112 _strxfrm::
113  jp 0
114 _memchr::
115  jp 0
116 _strchr::
117  jp 0
118 _strcspn::
119  jp 0
120 _strpbrk::
121  jp 0
122 _strrchr::
123  jp 0
124 _strspn::
125  jp 0
126 _strstr::
127  jp 0
128 _strtok::
129  jp 0
130 _memset::
131  jp 0
132 _strlen::
133  jp 0
134 _atoi::
135  jp 0
136 _atol::
137  jp 0
138 _rand::
139  jp 0
140 _randSeed::
141  jp 0
142 _abs::
143  jp 0
144 _labs::
145  jp 0
146 _isblank::
147  jp 0
148 _isdigit::
149  jp 0
150 _islower::
151  jp 0
152 _isupper::
153  jp 0
154 _isalnum::
155  jp 0
156 _isalpha::
157  jp 0
158 _iscntrl::
159  jp 0
160 _isgraph::
161  jp 0
162 _isprint::
163  jp 0
164 _ispunct::
165  jp 0
166 _isspace::
167  jp 0
168 _isxdigit::
169  jp 0
170 _tolower::
171  jp 0
172 _toupper::
173  jp 0
174 _cVectorVersion::
175  .dw 0x0101
176 
177  ; 4000-4020 unused for now since intel Hex might overlap 4000
178 
179  ;; required c vector version the applet expects
180  ;; C program must provide a const uint16_t  CVECTOR_VERSION = 0x0101; // adjust accordingly
181  .globl _CVECTOR_VERSION
182 
183  .org 0x4020
184  .dw _CVECTOR_VERSION
185  
186  ;; pointer to name of the applet
187  ;; C program must provide a const char* const APPLET_NAME = "applet name here";
188  .globl _APPLET_NAME
189  .org 0x4022
190  .dw _APPLET_NAME
191 
192  ;; pointer to name of the applet
193  ;; C program must provide a const char* const APPLET_NAME = "applet name here";
194  .globl _APPLET_TIMESTAMP
195  .org 0x4024
196  .dw _APPLET_TIMESTAMP
197 
198  ;; applet indicator--is these aren't 1,2,3,4, then the flash block is not an applet
199  .org 0x4026
200  .dw 0x0102
201  .dw 0x0304
202 
203  .org 0x4030
204 init:
205  ;; Set stack pointer directly above top of memory.
206  ld hl, #0
207  add hl, sp
208  ld sp,#0xc000
209  ;; Create a space to be used by bank switcher
210  push hl
211 
212  ;; store the SP from the code that called this
213  push hl
214 
215  ;; Initialise global variables
216  call gsinit
217 
218  call _main
219 
220  ; return to code that called us
221  pop hl
222  ld sp, hl
223 
224  ret
225 
226  ;; Ordering of segments for the linker.
227  .area _HOME
228  .area _CODE
229  .area _INITIALIZER
230  .area   _GSINIT
231  .area   _GSFINAL
232 
233  .area _DATA
234  .area _INITIALIZED
235  .area _BSEG
236  .area   _BSS
237  .area   _HEAP
238 
239  .area   _CODE
240 
241  .area   _GSINIT
242 gsinit::
243  ld bc, #l__INITIALIZER
244  ld a, b
245  or a, c
246  jr Z, gsinit_next
247  ld de, #s__INITIALIZED
248  ld hl, #s__INITIALIZER
249  ldir
250 gsinit_next:
251 
252  .area   _GSFINAL
253  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:

 1 #include <stdio.h>
 2 #include "uart.h"
 3 #include "timer.h"
 4 
 5 const char* const APPLET_NAME = "clock";
 6 const char* const APPLET_TIMESTAMP = __TIMESTAMP__;
 7 const uint16_t CVECTOR_VERSION = 0x0101;
 8 
 9 int clockId = -1;
10 uint8_t hour;
11 uint8_t min;
12 uint8_t sec;
13 
14 void showClock()
15 {
16     while (sec > 59)
17     {
18         sec = 0;
19         min++;
20     }
21     while (min > 59)
22     {
23         min = 0;
24         hour++;
25     }
26     while (hour > 12)
27     {
28         hour = 1;
29     }
30     printf("%02hd:%02hd:%02hd\r\n", hour, min, sec);
31 }
32 
33 void clockCB(int timerId)
34 {
35     timerId;
36     sec++;
37     showClock();
38 }
39 
40 void main()
41 {
42     char c;
43     bool go = false;
44 
45     uartSupressRTS(false, false);
46 
47     if (clockId == -1)
48     {
49         clockId = timerGetAvailableTimerId();
50     }
51     if (clockId == -1)
52     {
53         return;
54     }
55 
56     timerSet(clockId, 1000, true, false, clockCB);
57 
58     puts("'h' for hour, 'm' for minutes 'g' to go, 's' to stop");
59     clockCB(clockId);
60     while (!go)
61     {
62         c = getchar();
63         switch (c)
64         {
65         case 'h':
66             hour++;
67             break;
68         case 'm':
69             min++;
70             break;
71         case 'M':
72             min += 10;
73             break;
74         case 'g':
75             timerRun(clockId);
76             go = true;
77             break;
78         case 's':
79             timerStop(clockId);
80             go = true;
81             break;
82         }
83         showClock();
84     }
85 
86 }

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.