DS800C400 fun, improved ROM interface and tinibios
[fw/sdcc] / device / lib / ds400 / tinibios.c
1 /*-------------------------------------------------------------------------
2   tinibios.c - startup and serial routines for the DS80C400 (tested on TINIM400)
3   
4    Written By - Johan Knol, johan.knol@iduna.nl
5    
6    Further hacked by Kevin Vigor with invaluable assistance from Bob Heise.
7     
8    This program is free software; you can redistribute it and/or modify it
9    under the terms of the GNU General Public License as published by the
10    Free Software Foundation; either version 2, or (at your option) any
11    later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21    
22    In other words, you are welcome to use, share and improve this program.
23    You are forbidden to forbid anyone else to use, share and improve
24    what you give them.   Help stamp out software-hoarding!  
25 -------------------------------------------------------------------------*/
26
27 #include <tinibios.h>
28 #include <ds400rom.h>
29
30 #include <stdio.h>
31
32 #define TIMED_ACCESS(sfr,value) { TA=0xaa; TA=0x55; sfr=value; }
33
34 unsigned char _sdcc_external_startup(void)
35 {
36     IE = 0; // Disable all interrupts.
37
38     PSW = 0;
39     
40   _asm
41     ; save the 24-bit return address
42     pop ar2; msb
43     pop ar1
44     pop ar0; lsb
45
46
47     mov _ESP,#0x00; reinitialize the stack
48     mov _SP,#0x00
49
50     ; restore the 24-bit return address
51     push ar0; lsb
52     push ar1
53     push ar2; msb
54   _endasm;    
55     
56     // Stub: call rom_init here, then fixup IVT.
57     // 
58     
59     Serial0Init(1, 0); // baud argument ignored.
60     
61     IE = 0x80; // Enable interrupts.
62     
63     return 0;
64 }
65
66 // now the serial0 stuff
67
68 // just to make the code more readable 
69 #define S0RBS SERIAL_0_RECEIVE_BUFFER_SIZE
70
71 // this is a ring buffer and can overflow at anytime!
72 static volatile unsigned char receive0Buffer[S0RBS];
73 static volatile int receive0BufferHead=0;
74 static volatile int receive0BufferTail=0;
75 // no buffering for transmit
76 static volatile char transmit0IsBusy=0;
77
78 static data unsigned char serial0Buffered;
79
80 /* Initialize serial0.
81
82    Available baudrates are from 110 upto 115200 (using 16-bit timer 2)
83    If baud==0, the port is disabled.
84
85    If buffered!=0, characters received are buffered using an interrupt
86 */
87
88
89 #define TIMER_RELOAD (65536 - ((OSCILLATOR) / (32 * SERIAL_0_BAUD)))
90
91 void Serial0Init (unsigned long baud, unsigned char buffered) {
92   
93   ES0 = 0; // disable serial channel 0 interrupt
94
95 #if 0    
96   // Need no port setup, done by boot rom.
97   baud;
98 #else
99     SCON0 = 0x5A; // 10 bit serial 0, use timer baud rate, enable recieving
100     RCAP2H = (TIMER_RELOAD >> 8) & 0xff;
101     RCAP2L = TIMER_RELOAD & 0xff;
102     T2CON = 0x30; // Enable timer 2 for serial port
103     TR2 = 1; // Set timer 2 to run
104     
105     baud;
106 #endif    
107
108   serial0Buffered=buffered;
109  
110  if (buffered) {
111     installInterrupt(Serial0IrqHandler, 0x23);
112     RI_0=TI_0=0; // clear "pending" interrupts
113     ES0 = 1; // enable serial channel 0 interrupt
114   } else {
115     RI_0=0; // receive buffer empty
116     TI_0=1; // transmit buffer empty
117   }
118 }
119
120 void Serial0SwitchToBuffered(void)
121 {
122     IE &= ~0x80;
123     
124     serial0Buffered = 1;
125     installInterrupt(Serial0IrqHandler, 0x23);
126     RI_0=TI_0=0; // clear "pending" interrupts
127     ES0 = 1; // enable serial channel 0 interrupt
128     
129     IE |= 0x80;
130 }
131
132 void Serial0IrqHandler (void) interrupt 4 {
133   if (RI_0) {
134     receive0Buffer[receive0BufferHead]=SBUF0;
135     receive0BufferHead=(receive0BufferHead+1)&(S0RBS-1);
136     if (receive0BufferHead==receive0BufferTail) {
137       /* buffer overrun, sorry :) */
138       receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
139     }
140     RI_0=0;
141   }
142   if (TI_0) {
143     TI_0=0;
144     transmit0IsBusy=0;
145   }
146 }
147
148 char Serial0CharArrived(void) {
149   if (serial0Buffered) {
150     if (receive0BufferHead!=receive0BufferTail)
151       return receive0Buffer[receive0BufferTail];
152   } else {
153     if (RI_0)
154       return SBUF0;
155   }
156   return 0;
157 }
158
159 void Serial0PutChar (char c)
160 {
161   if (serial0Buffered) {
162     while (transmit0IsBusy)
163       ;
164     transmit0IsBusy=1;
165     SBUF0=c;
166   } else {
167     while (!TI_0)
168       ;
169     TI_0 = 0;
170     SBUF0=c;
171   }
172 }
173
174 char Serial0GetChar (void)
175 {
176   char c;
177   if (serial0Buffered) {
178     while (receive0BufferHead==receive0BufferTail)
179       ;
180     c=receive0Buffer[receive0BufferTail];
181     ES0=0; // disable serial interrupts
182     receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
183     ES0=1; // enable serial interrupts
184   } else {
185     while (!RI_0)
186       ;
187     c=SBUF0;
188     RI_0=0;
189   }
190   return c;
191 }
192
193 void Serial0SendBreak() {
194   P3 &= ~0x02;
195   ClockMilliSecondsDelay(2);
196   P3 |= 0x02;
197 }
198
199 void Serial0Flush() {
200   ES0=0; // disable interrupts
201   receive0BufferHead=receive0BufferTail=0;
202   RI_0=0;
203   if (serial0Buffered) {
204     TI_0=0;
205     ES0=1; // enable interrupts
206   } else {
207     TI_0=1;
208   }
209 }
210
211 // now let's go for the clock stuff - on the DS400, we can just
212 // use the ROM's millisecond timer, running off timer 0.
213 // 
214 // for now, this timer runs too fast by about 20%. We need an implementation of
215 // task_settickreload to fix this.
216
217 void ClockInit() {
218     // nada, all done by DSS_rom_init
219 }
220
221 // we can't just use milliSeconds
222 unsigned long ClockTicks(void) {
223     return task_gettimemillis_long();
224 }
225
226 void ClockMilliSecondsDelay(unsigned long delay) {
227   unsigned long ms = task_gettimemillis_long() + delay;
228
229     while (ms > task_gettimemillis_long())
230         ;
231 }
232
233 // Return the start of the XI_SEG. Really just a workaround for the
234 // fact that the linker defined symbol (s_XISEG) isn't directly accessible
235 // from C due to the lack of a leading underscore, and I'm too lazy to hack 
236 // the linker.
237 static void xdata *_xisegStart(void) _naked
238 {
239 _asm    
240         mov     dptr, #(s_XISEG)
241         ret
242 _endasm;
243 }
244
245 // Return the length of the XI_SEG. Really just a workaround for the
246 // fact that the linker defined symbol (l_XISEG) isn't directly accessible
247 // from C due to the lack of a leading underscore, and I'm too lazy to hack 
248 // the linker.
249 static unsigned  _xisegLen(void) _naked
250 {
251 _asm    
252         mov     dptr, #(l_XISEG)
253         ret
254 _endasm;
255 }
256
257 // Returns the address of the first byte available for heap memory, 
258 // i.e. the first byte following the XI_SEG.
259 static void xdata *_firstHeapByte(void)
260 {
261     unsigned char xdata *start;
262     
263     start = (unsigned char xdata *) _xisegStart();      
264     start += _xisegLen();
265
266     return (void xdata *)start;
267 }
268
269 // TINIm400 specific startup.
270
271 // The last addressible byte of the CE0 area. 
272 #define CE0_END 0xfffff
273
274 unsigned char romInit(unsigned char noisy,
275                       char           speed)
276 {
277     void xdata *heapStart;
278     void xdata *heapEnd;
279     unsigned long heapLen; 
280     unsigned char rc;
281
282     if (speed == SPEED_2X)
283     {
284         PMR = 0x82;
285         PMR = 0x92;
286
287         while (!(EXIF & 8))
288             ;
289
290         PMR = 0x12;
291     }
292     else if (speed == SPEED_4X)
293     {
294         // Hangs on TINIm400!
295         PMR = 0x82;
296         PMR = 0x8a;
297         PMR = 0x9a;
298
299         while (!(EXIF & 8))
300             ;
301
302         PMR = 0x1a;
303     }
304     
305     heapStart = _firstHeapByte();
306     heapEnd = (void xdata *)CE0_END;
307
308     rc = init_rom(heapStart, heapEnd);
309     
310     if (noisy)
311     {
312         if (rc)
313         {
314             printf("error: rom_init returns %d\n", (int)rc);
315             return rc;
316         }
317         else
318         {
319             heapLen = CE0_END - (unsigned long)heapStart;
320             printf("Heap starts at %p, length %luK\n", heapStart, heapLen / 1024);
321         }
322     }
323     
324     task_settickreload(RELOAD_14_746);
325     
326     // Switch to interrupt driven serial I/O now that the rom is initialized.
327     Serial0SwitchToBuffered();
328     
329     P5 &= ~4; // LED on.
330     
331     return 0;
332 }
333
334 // Install an interrupt handler.
335 void installInterrupt(void (*isrPtr)(void), unsigned char offset)
336 {
337     unsigned char xdata * vectPtr = (unsigned char xdata *) offset;
338     unsigned long isr = (unsigned long)isrPtr;
339
340     *vectPtr++ = 0x02;
341     *vectPtr++ = (unsigned char)(isr >> 16);
342     *vectPtr++ = (unsigned char)(isr >> 8);
343     *vectPtr = (unsigned char)isr;
344 }