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:
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.
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.