3a8e0f3e7390a1d8c4fc9ea07b074cc5ca6c509b
[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 {
134   if (RI_0) {
135     receive0Buffer[receive0BufferHead]=SBUF0;
136     receive0BufferHead=(receive0BufferHead+1)&(S0RBS-1);
137     if (receive0BufferHead==receive0BufferTail) {
138       /* buffer overrun, sorry :) */
139       receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
140     }
141     RI_0=0;
142   }
143   if (TI_0) {
144     TI_0=0;
145     transmit0IsBusy=0;
146   }
147 }
148
149 char Serial0CharArrived(void)
150 {
151   if (serial0Buffered) {
152     if (receive0BufferHead!=receive0BufferTail)
153       return receive0Buffer[receive0BufferTail];
154   } else {
155     if (RI_0)
156       return SBUF0;
157   }
158   return 0;
159 }
160
161 void Serial0PutChar (char c)
162 {
163   if (serial0Buffered) {
164     while (transmit0IsBusy)
165       ;
166     transmit0IsBusy=1;
167     SBUF0=c;
168   } else {
169     while (!TI_0)
170       ;
171     TI_0 = 0;
172     SBUF0=c;
173   }
174 }
175
176 char Serial0GetChar (void)
177 {
178   char c;
179   if (serial0Buffered) {
180     while (receive0BufferHead==receive0BufferTail)
181       ;
182     c=receive0Buffer[receive0BufferTail];
183     ES0=0; // disable serial interrupts
184     receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
185     ES0=1; // enable serial interrupts
186   } else {
187     while (!RI_0)
188       ;
189     c=SBUF0;
190     RI_0=0;
191   }
192   return c;
193 }
194
195 void Serial0SendBreak()
196 {
197   P3 &= ~0x02;
198   ClockMilliSecondsDelay(2);
199   P3 |= 0x02;
200 }
201
202 void Serial0Flush()
203 {
204   ES0=0; // disable interrupts
205   receive0BufferHead=receive0BufferTail=0;
206   RI_0=0;
207   if (serial0Buffered) {
208     TI_0=0;
209     ES0=1; // enable interrupts
210   } else {
211     TI_0=1;
212   }
213 }
214
215 // now let's go for the clock stuff - on the DS400, we can just
216 // use the ROM's millisecond timer, running off timer 0.
217 // 
218 // for now, this timer runs too fast by about 20%. We need an implementation of
219 // task_settickreload to fix this.
220
221 void ClockInit()
222 {
223   // nada, all done by DSS_rom_init
224 }
225
226 // we can't just use milliSeconds
227 unsigned long ClockTicks(void)
228 {
229   return task_gettimemillis_long();
230 }
231
232 void ClockMilliSecondsDelay(unsigned long delay)
233 {
234   unsigned long ms = task_gettimemillis_long() + delay;
235
236   while (ms > task_gettimemillis_long())
237     ;
238 }
239
240 // Return the start of the XI_SEG. Really just a workaround for the
241 // fact that the linker defined symbol (s_XISEG) isn't directly accessible
242 // from C due to the lack of a leading underscore, and I'm too lazy to hack 
243 // the linker.
244 static void __xdata *_xisegStart(void) __naked
245 {
246   __asm    
247   mov dptr, #(s_XISEG)
248   ret
249   __endasm;
250 }
251
252 // Return the length of the XI_SEG. Really just a workaround for the
253 // fact that the linker defined symbol (l_XISEG) isn't directly accessible
254 // from C due to the lack of a leading underscore, and I'm too lazy to hack 
255 // the linker.
256 static unsigned  _xisegLen(void) __naked
257 {
258   __asm
259   mov dptr, #(l_XISEG)
260   ret
261   __endasm;
262 }
263
264 // Returns the address of the first byte available for heap memory, 
265 // i.e. the first byte following the XI_SEG.
266 static void __xdata *_firstHeapByte(void)
267 {
268   unsigned char __xdata *start;
269   
270   start = (unsigned char __xdata *) _xisegStart();
271   start += _xisegLen();
272
273   return (void __xdata *)start;
274 }
275
276 // TINIm400 specific startup.
277
278 // The last addressible byte of the CE0 area. 
279 #define CE0_END 0xfffff
280
281 unsigned char romInit(unsigned char noisy, char speed)
282 {
283   void __xdata *heapStart;
284   void __xdata *heapEnd;
285   unsigned long heapLen; 
286   unsigned char rc;
287
288   if (speed == SPEED_2X)
289   {
290     PMR = 0x82;
291     PMR = 0x92;
292
293     while (!(EXIF & 8))
294       ;
295
296     PMR = 0x12;
297   }
298   else if (speed == SPEED_4X)
299   {
300     // Hangs on TINIm400!
301     PMR = 0x82;
302     PMR = 0x8a;
303     PMR = 0x9a;
304
305     while (!(EXIF & 8))
306       ;
307
308     PMR = 0x1a;
309   }
310
311   heapStart = _firstHeapByte();
312   heapEnd = (void __xdata *)CE0_END;
313
314   rc = init_rom(heapStart, heapEnd);
315
316   if (noisy)
317   {
318     if (rc)
319     {
320       printf("error: rom_init returns %d\n", (int)rc);
321       return rc;
322     }
323     else
324     {
325       heapLen = CE0_END - (unsigned long)heapStart;
326       printf("Heap starts at %p, length %luK\n", heapStart, heapLen / 1024);
327     }
328   }
329
330   task_settickreload(RELOAD_14_746);
331
332   // Switch to interrupt driven serial I/O now that the rom is initialized.
333   Serial0SwitchToBuffered();
334
335   P5 &= ~4; // LED on.
336
337   return 0;
338 }
339
340 // Install an interrupt handler.
341 void installInterrupt(void (*isrPtr)(void), unsigned char offset)
342 {
343   unsigned char __xdata * vectPtr = (unsigned char __xdata *) offset;
344   unsigned long isr = (unsigned long)isrPtr;
345
346   *vectPtr++ = 0x02;
347   *vectPtr++ = (unsigned char)(isr >> 16);
348   *vectPtr++ = (unsigned char)(isr >> 8);
349   *vectPtr = (unsigned char)isr;
350 }