Use sparkly new _naked attribute
[fw/sdcc] / device / lib / ds390 / tinibios.c
1 /*-------------------------------------------------------------------------
2   tinibios.c - startup and serial routines for the DS80C390 (tested on TINI)
3   
4    Written By - Johan Knol, johan.knol@iduna.nl
5     
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19    
20    In other words, you are welcome to use, share and improve this program.
21    You are forbidden to forbid anyone else to use, share and improve
22    what you give them.   Help stamp out software-hoarding!  
23 -------------------------------------------------------------------------*/
24
25 #include <tinibios.h>
26 #include <stdio.h>
27
28 #define TIMED_ACCESS(sfr,value) { TA=0xaa; TA=0x55; sfr=value; }
29  
30 unsigned char _sdcc_external_startup(void)
31 {
32   IE=0; // disable ALL interrupts
33
34   // use A19..16 and !CE3..0, no CAN
35   TIMED_ACCESS(P4CNT,0x3f);
36
37   // use !PCE3..0, serial 1 at P5.2/3
38   TIMED_ACCESS(P5CNT,0x27);
39
40   // disable watchdog
41   EWT=0;
42
43   // watchdog set to 9.1 seconds
44   // CKCON|=0xc0;
45
46   // default stretch cycles for MOVX
47   //CKCON = (CKCON&0xf8)|(CPU_MOVX_STRETCH&0x07);
48   CKCON=0xf9;
49
50   // use internal 4k RAM as data(stack) memory at 0x400000 and
51   // move CANx memory access to 0x401000 and upwards
52   // use !CE* for program and/or data memory access
53   TIMED_ACCESS(MCON,0xaf);
54
55   // select default cpu speed
56   CpuSpeed(CPU_SPEED);
57
58   _asm
59     ; save the 24-bit return address
60     pop ar2; msb
61     pop ar1
62     pop ar0; lsb
63
64
65     mov _TA,#0xaa; timed access
66     mov _TA,#0x55
67     mov _ACON,#0x06; 24 bit addresses, 10 bit stack at 0x400000
68
69     mov _ESP,#0x00; reinitialize the stack
70     mov _SP,#0x00
71
72     ; restore the 24-bit return address
73     push ar0; lsb
74     push ar1
75     push ar2; msb
76   _endasm;
77
78   // Copy the Interrupt Vector Table (128 bytes) from 0x10000 to 0x100000
79   // This isn't needed for older bootloaders than the 0515, but it won't harm
80   _asm
81   push dpx
82   push dph
83   push dpl
84   push dps
85   push b
86   push acc
87   mov dps,#0x00 ; make sure no autoincrement in progress
88   mov dptr,#0x10000 ; from
89   inc dps ; switch to alternate dptr
90   mov dptr,#0x100000 ; to
91   mov b,#0x80 ; count
92
93 _Startup390CopyIVT:
94   inc dps
95   movx a,@dptr
96   inc dptr
97   inc dps
98   movx @dptr,a
99   inc dptr
100   djnz b,_Startup390CopyIVT
101
102   pop acc
103   pop b
104   pop dps
105   pop dpl
106   pop dph
107   pop dpx
108   _endasm;
109
110   // global interrupt enable, all masks cleared
111   // let the Gods be with us :)
112   IE = 0x80; 
113
114   Serial0Init(SERIAL_0_BAUD,1);
115   //Serial1Init(SERIAL_1_BAUD,1);
116   ClockInit();
117   //RtcInit();
118   //WatchDogInit();
119
120   // signal _sdcc_gsinit_startup to initialize data (call _sdcc_init_data)
121   return 0; 
122 }
123
124 /* Set the cpu speed in clocks per machine cycle, valid values are:
125    1024: Power management mode
126       4: Divide-by-4 mode
127       2: Use frequency multiplier (2x)
128       1: Use frequency multiplier (4x) (Don't do this on a TINI at 18.432MHz)
129
130    TODO: TINI seems to support only 2 and 4: write only bits in PMR ?
131 */
132
133 unsigned int cpuSpeed;
134
135 void CpuSpeed(unsigned int speed) {
136
137   while (0 && (EXIF&0x04))
138     ; // cpu operates from ring buffer
139   PMR = 0x80; // div4, CTM off, multiplier 2x
140   switch (speed) 
141     {
142     case 1:
143       PMR=0x88; // div4, CTM off, multiplier 4x
144       PMR=0x98; // div4, CTM on, multiplier 4x
145       while ((EXIF&0x08)==0) {
146         ; // wait for the multiplier to be ready
147       }
148       PMR = 0x18; // use multiplier
149       cpuSpeed=speed;
150       break;
151     case 2:
152       PMR=0x90; // div4, CTM on, multilier 2x
153       while ((EXIF&0x08)==0) {
154         ; // wait for the multiplier to be ready
155       }
156       PMR = 0x10; // use multiplier
157       cpuSpeed=speed;
158       break;
159     case 4:
160       // nothing to do
161       cpuSpeed=speed;
162       break;
163     case 1024:
164       PMR = 0xc0; // div1024, CTM off
165       cpuSpeed=speed;
166       break;
167     }
168 }  
169
170 // now the serial0 stuff
171
172 // just to make the code more readable 
173 #define S0RBS SERIAL_0_RECEIVE_BUFFER_SIZE
174
175 // this is a ring buffer and can overflow at anytime!
176 static volatile unsigned char receive0Buffer[S0RBS];
177 static volatile int receive0BufferHead=0;
178 static volatile int receive0BufferTail=0;
179 // no buffering for transmit
180 static volatile char transmit0IsBusy=0;
181
182 static data unsigned char serial0Buffered;
183
184 /* Initialize serial0.
185
186    Available baudrates are from 110 upto 115200 (using 16-bit timer 2)
187    If baud==0, the port is disabled.
188
189    If buffered!=0, characters received are buffered using an interrupt
190 */
191
192 void Serial0Init (unsigned long baud, unsigned char buffered) {
193   
194   if (baud==0) {
195     ES0=0; // disable interrupts
196     SCON0 &= 0xef; // disable receiver
197     return;
198   }
199
200   ES0 = 0; // disable serial channel 0 interrupt
201   TR2 = 0; // stop timer 2
202   
203   // set 8 bit uart with variable baud from timer 1/2
204   // enable receiver and clear RI and TI
205   SCON0 = 0x50;
206   
207   PCON |= 0x80; // clock is 16x bitrate
208   CKCON|=0x20; // timer uses xtal/4
209   
210   T2MOD=0; // no fancy functions
211   T2CON=0x34; // start timer as a baudrate generator for serial0
212   
213   // set the baud rate
214   Serial0Baud(baud);
215   
216   serial0Buffered=buffered;
217  
218  if (buffered) {
219     RI_0=TI_0=0; // clear "pending" interrupts
220     ES0 = 1; // enable serial channel 0 interrupt
221   } else {
222     RI_0=0; // receive buffer empty
223     TI_0=1; // transmit buffer empty
224   }
225 }
226
227 void Serial0Baud(unsigned long baud) {
228   TR2=0; // stop timer
229   baud=-((long)OSCILLATOR/(32*baud));
230   TL2=RCAP2L= baud;
231   TH2=RCAP2H= baud>>8;
232   TF2=0; // clear overflow flag
233   TR2=1; // start timer
234 }  
235
236 void Serial0IrqHandler (void) interrupt 4 {
237   if (RI_0) {
238     receive0Buffer[receive0BufferHead]=SBUF0;
239     receive0BufferHead=(receive0BufferHead+1)&(S0RBS-1);
240     if (receive0BufferHead==receive0BufferTail) {
241       /* buffer overrun, sorry :) */
242       receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
243     }
244     RI_0=0;
245   }
246   if (TI_0) {
247     TI_0=0;
248     transmit0IsBusy=0;
249   }
250 }
251
252 char Serial0CharArrived(void) {
253   if (serial0Buffered) {
254     if (receive0BufferHead!=receive0BufferTail)
255       return receive0Buffer[receive0BufferTail];
256   } else {
257     if (RI_0)
258       return SBUF0;
259   }
260   return 0;
261 }
262
263 void Serial0PutChar (char c)
264 {
265   if (serial0Buffered) {
266     while (transmit0IsBusy)
267       ;
268     transmit0IsBusy=1;
269     SBUF0=c;
270   } else {
271     while (!TI_0)
272       ;
273     SBUF0=c;
274     TI_0=0;
275   }
276 }
277
278 char Serial0GetChar (void)
279 {
280   char c;
281   if (serial0Buffered) {
282     while (receive0BufferHead==receive0BufferTail)
283       ;
284     c=receive0Buffer[receive0BufferTail];
285     ES0=0; // disable serial interrupts
286     receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
287     ES0=1; // enable serial interrupts
288   } else {
289     while (!RI_0)
290       ;
291     c=SBUF0;
292     RI_0=0;
293   }
294   return c;
295 }
296
297 void Serial0SendBreak() {
298   P3 &= ~0x02;
299   ClockMilliSecondsDelay(2);
300   P3 |= 0x02;
301 }
302
303 void Serial0Flush() {
304   ES0=0; // disable interrupts
305   receive0BufferHead=receive0BufferTail=0;
306   RI_0=0;
307   if (serial0Buffered) {
308     TI_0=0;
309     ES0=1; // enable interrupts
310   } else {
311     TI_0=1;
312   }
313 }
314
315 /* now let's go for the serial1 stuff, basically it's a replicate of 
316    serial0 except it uses timer 1
317 */
318
319 // just to make the code more readable 
320 #define S1RBS SERIAL_1_RECEIVE_BUFFER_SIZE
321
322 // this is a ring buffer and can overflow at anytime!
323 static volatile unsigned char receive1Buffer[S1RBS];
324 static volatile int receive1BufferHead=0;
325 static volatile int receive1BufferTail=0;
326 // no buffering for transmit
327 static volatile char transmit1IsBusy=0;
328
329 static data unsigned char serial1Buffered;
330
331 /* Initialize serial1.
332
333    Available baudrates are from 4800 upto 115200 (using 8-bit timer 1)
334    If baud==0, the port is disabled.
335
336    If buffered!=0, characters received are buffered using an interrupt
337 */
338
339 void Serial1Init (unsigned long baud, unsigned char buffered) {
340   
341   if (baud==0) {
342     ES1=0; // disable interrupt
343     SCON1 &= 0xef; // disable receiver
344     return; // and don't touch it
345   }
346
347   ES1 = 0; // disable channel 1 interrupt
348   TR1 = 0; // stop timer 1
349   
350   // set 8 bit uart with variable baud from timer 1
351   // enable receiver and clear RI and TI
352   SCON1 = 0x50;
353   
354   WDCON |= 0x80; // clock is 16x bitrate
355   CKCON|=0x10; // timer uses xtal/4
356   
357   TMOD = (TMOD&0x0f) | 0x20; // timer 1 is an 8bit auto-reload counter
358   
359   // set the baud rate
360   Serial1Baud(baud);
361   
362   serial1Buffered=buffered;
363
364   if (buffered) {
365     RI_1=TI_1=0; // clear "pending" interrupts
366     ES1 = 1; // enable serial channel 1 interrupt
367   } else {
368     RI_1=0; // receive buffer empty
369     TI_1=1; // transmit buffer empty
370   }
371 }
372
373 void Serial1Baud(unsigned long baud) {
374   TR1=0; // stop timer
375   baud=-((long)OSCILLATOR/(32*baud));
376   TL1=TH1 = baud;
377   TF1=0; // clear overflow flag
378   TR1=1; // start timer
379 }  
380
381 void Serial1IrqHandler (void) interrupt 7 {
382   if (RI_1) {
383     receive1Buffer[receive1BufferHead]=SBUF1;
384     receive1BufferHead=(receive1BufferHead+1)&(S1RBS-1);
385     if (receive1BufferHead==receive1BufferTail) /* buffer overrun, sorry :) */
386       receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
387     RI_1=0;
388   }
389   if (TI_1) {
390     TI_1=0;
391     transmit1IsBusy=0;
392   }
393 }
394
395 char Serial1CharArrived(void) {
396   if (serial1Buffered) {
397     if (receive1BufferHead!=receive1BufferTail)
398       return receive1Buffer[receive1BufferTail];
399   } else {
400     if (RI_1)
401       return SBUF1;
402   }
403   return 0;
404 }
405
406 void Serial1PutChar (char c)
407 {
408   if (serial1Buffered) {
409     while (transmit1IsBusy)
410       ;
411     transmit1IsBusy=1;
412     SBUF1=c;
413   } else {
414     while (!TI_1)
415       ;
416     SBUF1=c;
417     TI_1=0;
418   }
419 }
420
421 char Serial1GetChar (void)
422 {
423   char c;
424   if (serial1Buffered) {
425     while (receive1BufferHead==receive1BufferTail)
426       ;
427     c=receive1Buffer[receive1BufferTail];
428     ES1=0; // disable serial interrupts
429     receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
430     ES1=1; // enable serial interrupts
431   } else {
432     while (!RI_1)
433       ;
434     c=SBUF1;
435     RI_1=0;
436   }
437   return c;
438 }
439
440 void Serial1SendBreak() {
441   P5 &= ~0x08;
442   ClockMilliSecondsDelay(2);
443   P5 |= 0x08;
444 }
445
446 void Serial1Flush() {
447   ES1=0; // disable interrupts
448   receive1BufferHead=receive1BufferTail=0;
449   RI_1=0;
450   if (serial1Buffered) {
451     TI_1=0;
452     ES1=1; // enable interrupts
453   } else {
454     TI_1=1;
455   }
456 }
457
458 // now let's go for the clock stuff
459
460 // these REALLY need to be in data space for the irq routine!
461 static data unsigned long milliSeconds=0;
462 static data unsigned int timer0ReloadValue;
463
464 void ClockInit() {
465   unsigned long timerReloadValue=OSCILLATOR/1000;
466
467   switch (cpuSpeed) {
468   case 4: timerReloadValue/=4; break;
469   case 1: // not tested yet
470   case 2:  // not tested yet
471   default: timerReloadValue/=2; break;
472   }
473   timer0ReloadValue=~timerReloadValue;
474   // initialise timer 0
475   ET0=0; // disable timer interrupts initially
476   TCON = (TCON&0xcc)|0x00; // stop timer, clear overflow
477   TMOD = (TMOD&0xf0)|0x01; // 16 bit counter
478   CKCON|=0x08; // timer uses xtal/4
479   
480   TL0=timer0ReloadValue&0xff;
481   TH0=timer0ReloadValue>>8;
482   
483   ET0=1; // enable timer interrupts
484   TR0=1; // start timer
485 }
486
487 // This needs to be SUPER fast. What we really want is:
488
489 #if 0
490 void junk_ClockIrqHandler (void) interrupt 10 {
491   TL0=timer0ReloadValue&0xff;
492   TH0=timer0ReloadValue>>8;
493   milliSeconds++;
494 }
495 #else
496 // but look at the code, and the pushes and pops, so:
497 void ClockIrqHandler (void) interrupt 1 _naked
498 {
499   _asm
500     push acc
501     mov _TL0,_timer0ReloadValue
502     mov _TH0,_timer0ReloadValue+1
503     clr a
504     inc _milliSeconds+0
505     cjne a,_milliSeconds+0,_ClockIrqHandlerDone
506     inc _milliSeconds+1
507     cjne a,_milliSeconds+1,_ClockIrqHandlerDone
508     inc _milliSeconds+2
509     cjne a,_milliSeconds+2,_ClockIrqHandlerDone
510     inc _milliSeconds+3
511    _ClockIrqHandlerDone:
512     pop acc
513     reti
514   _endasm;
515 }
516 #endif
517
518 // we can't just use milliSeconds
519 unsigned long ClockTicks(void) {
520   unsigned long ms;
521   ET0=0;
522   ms=milliSeconds;
523   ET0=1;
524   return ms;
525 }
526
527 void ClockMilliSecondsDelay(unsigned long delay) {
528   long ms=ClockTicks()+delay;
529
530   while (ms>ClockTicks())
531     ;
532 }
533
534 // stolen from Kevin Vigor, works only for TINI at default speed
535 void ClockMicroSecondsDelay(unsigned int delay) {
536    delay; /* shut compiler up. */
537    
538    _asm
539      
540    ; delay is in dpl/dph
541    mov  r0, dpl
542    mov  r1, dph
543    
544    mov  a, r0
545    orl  a, r1                   ; quick out for zero case.
546    jz   _usDelayDone
547    
548    inc  r1
549    cjne r0, #0, _usDelayLoop
550    dec  r1
551    
552    _usDelayLoop:
553    nop
554    nop
555    nop
556    nop
557    nop
558    nop
559    nop                          ; 7 nops
560    djnz r0, _usDelayLoop        ; 3 cycles x 1 = 3 cycles
561                                 ; 10 cycles per iter
562                                 ; we want 9.216, but more is better
563                                 ; than less.
564    djnz r1, _usDelayLoop        
565 _usDelayDone:
566    
567    _endasm;
568   
569 }