* configure,
[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   #if 0
138   while (0 && (EXIF&0x04))
139     ; // cpu operates from ring buffer
140   #endif
141   PMR = 0x80; // div4, CTM off, multiplier 2x
142   switch (speed) 
143     {
144     case 1:
145       PMR=0x88; // div4, CTM off, multiplier 4x
146       PMR=0x98; // div4, CTM on, multiplier 4x
147       while ((EXIF&0x08)==0) {
148         ; // wait for the multiplier to be ready
149       }
150       PMR = 0x18; // use multiplier
151       cpuSpeed=speed;
152       break;
153     case 2:
154       PMR=0x90; // div4, CTM on, multilier 2x
155       while ((EXIF&0x08)==0) {
156         ; // wait for the multiplier to be ready
157       }
158       PMR = 0x10; // use multiplier
159       cpuSpeed=speed;
160       break;
161     case 4:
162       // nothing to do
163       cpuSpeed=speed;
164       break;
165     case 1024:
166       PMR = 0xc0; // div1024, CTM off
167       cpuSpeed=speed;
168       break;
169     }
170 }  
171
172 // now the serial0 stuff
173
174 // just to make the code more readable 
175 #define S0RBS SERIAL_0_RECEIVE_BUFFER_SIZE
176
177 // this is a ring buffer and can overflow at anytime!
178 static volatile unsigned char receive0Buffer[S0RBS];
179 static volatile int receive0BufferHead=0;
180 static volatile int receive0BufferTail=0;
181 // no buffering for transmit
182 static volatile char transmit0IsBusy=0;
183
184 static data unsigned char serial0Buffered;
185
186 /* Initialize serial0.
187
188    Available baudrates are from 110 upto 115200 (using 16-bit timer 2)
189    If baud==0, the port is disabled.
190
191    If buffered!=0, characters received are buffered using an interrupt
192 */
193
194 void Serial0Init (unsigned long baud, unsigned char buffered) {
195   
196   if (baud==0) {
197     ES0=0; // disable interrupts
198     SCON0 &= 0xef; // disable receiver
199     return;
200   }
201
202   ES0 = 0; // disable serial channel 0 interrupt
203   TR2 = 0; // stop timer 2
204   
205   // set 8 bit uart with variable baud from timer 1/2
206   // enable receiver and clear RI and TI
207   SCON0 = 0x50;
208   
209   PCON |= 0x80; // clock is 16x bitrate
210   CKCON|=0x20; // timer uses xtal/4
211   
212   T2MOD=0; // no fancy functions
213   T2CON=0x34; // start timer as a baudrate generator for serial0
214   
215   // set the baud rate
216   Serial0Baud(baud);
217   
218   serial0Buffered=buffered;
219  
220  if (buffered) {
221     RI_0=TI_0=0; // clear "pending" interrupts
222     ES0 = 1; // enable serial channel 0 interrupt
223   } else {
224     RI_0=0; // receive buffer empty
225     TI_0=1; // transmit buffer empty
226   }
227 }
228
229 void Serial0Baud(unsigned long baud) {
230   TR2=0; // stop timer
231   baud=-((long)OSCILLATOR/(32*baud));
232   TL2=RCAP2L= baud;
233   TH2=RCAP2H= baud>>8;
234   TF2=0; // clear overflow flag
235   TR2=1; // start timer
236 }  
237
238 void Serial0IrqHandler (void) interrupt 4 {
239   if (RI_0) {
240     receive0Buffer[receive0BufferHead]=SBUF0;
241     receive0BufferHead=(receive0BufferHead+1)&(S0RBS-1);
242     if (receive0BufferHead==receive0BufferTail) {
243       /* buffer overrun, sorry :) */
244       receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
245     }
246     RI_0=0;
247   }
248   if (TI_0) {
249     TI_0=0;
250     transmit0IsBusy=0;
251   }
252 }
253
254 char Serial0CharArrived(void) {
255   if (serial0Buffered) {
256     if (receive0BufferHead!=receive0BufferTail)
257       return receive0Buffer[receive0BufferTail];
258   } else {
259     if (RI_0)
260       return SBUF0;
261   }
262   return 0;
263 }
264
265 void Serial0PutChar (char c)
266 {
267   if (serial0Buffered) {
268     while (transmit0IsBusy)
269       ;
270     transmit0IsBusy=1;
271     SBUF0=c;
272   } else {
273     while (!TI_0)
274       ;
275     SBUF0=c;
276     TI_0=0;
277   }
278 }
279
280 char Serial0GetChar (void)
281 {
282   char c;
283   if (serial0Buffered) {
284     while (receive0BufferHead==receive0BufferTail)
285       ;
286     c=receive0Buffer[receive0BufferTail];
287     ES0=0; // disable serial interrupts
288     receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
289     ES0=1; // enable serial interrupts
290   } else {
291     while (!RI_0)
292       ;
293     c=SBUF0;
294     RI_0=0;
295   }
296   return c;
297 }
298
299 void Serial0SendBreak() {
300   P3 &= ~0x02;
301   ClockMilliSecondsDelay(2);
302   P3 |= 0x02;
303 }
304
305 void Serial0Flush() {
306   ES0=0; // disable interrupts
307   receive0BufferHead=receive0BufferTail=0;
308   RI_0=0;
309   if (serial0Buffered) {
310     TI_0=0;
311     ES0=1; // enable interrupts
312   } else {
313     TI_0=1;
314   }
315 }
316
317 /* now let's go for the serial1 stuff, basically it's a replicate of 
318    serial0 except it uses timer 1
319 */
320
321 // just to make the code more readable 
322 #define S1RBS SERIAL_1_RECEIVE_BUFFER_SIZE
323
324 // this is a ring buffer and can overflow at anytime!
325 static volatile unsigned char receive1Buffer[S1RBS];
326 static volatile int receive1BufferHead=0;
327 static volatile int receive1BufferTail=0;
328 // no buffering for transmit
329 static volatile char transmit1IsBusy=0;
330
331 static data unsigned char serial1Buffered;
332
333 /* Initialize serial1.
334
335    Available baudrates are from 4800 upto 115200 (using 8-bit timer 1)
336    If baud==0, the port is disabled.
337
338    If buffered!=0, characters received are buffered using an interrupt
339 */
340
341 void Serial1Init (unsigned long baud, unsigned char buffered) {
342   
343   if (baud==0) {
344     ES1=0; // disable interrupt
345     SCON1 &= 0xef; // disable receiver
346     return; // and don't touch it
347   }
348
349   ES1 = 0; // disable channel 1 interrupt
350   TR1 = 0; // stop timer 1
351   
352   // set 8 bit uart with variable baud from timer 1
353   // enable receiver and clear RI and TI
354   SCON1 = 0x50;
355   
356   WDCON |= 0x80; // clock is 16x bitrate
357   CKCON|=0x10; // timer uses xtal/4
358   
359   TMOD = (TMOD&0x0f) | 0x20; // timer 1 is an 8bit auto-reload counter
360   
361   // set the baud rate
362   Serial1Baud(baud);
363   
364   serial1Buffered=buffered;
365
366   if (buffered) {
367     RI_1=TI_1=0; // clear "pending" interrupts
368     ES1 = 1; // enable serial channel 1 interrupt
369   } else {
370     RI_1=0; // receive buffer empty
371     TI_1=1; // transmit buffer empty
372   }
373 }
374
375 void Serial1Baud(unsigned long baud) {
376   TR1=0; // stop timer
377   baud=-((long)OSCILLATOR/(32*baud));
378   TL1=TH1 = baud;
379   TF1=0; // clear overflow flag
380   TR1=1; // start timer
381 }  
382
383 void Serial1IrqHandler (void) interrupt 7 {
384   if (RI_1) {
385     receive1Buffer[receive1BufferHead]=SBUF1;
386     receive1BufferHead=(receive1BufferHead+1)&(S1RBS-1);
387     if (receive1BufferHead==receive1BufferTail) /* buffer overrun, sorry :) */
388       receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
389     RI_1=0;
390   }
391   if (TI_1) {
392     TI_1=0;
393     transmit1IsBusy=0;
394   }
395 }
396
397 char Serial1CharArrived(void) {
398   if (serial1Buffered) {
399     if (receive1BufferHead!=receive1BufferTail)
400       return receive1Buffer[receive1BufferTail];
401   } else {
402     if (RI_1)
403       return SBUF1;
404   }
405   return 0;
406 }
407
408 void Serial1PutChar (char c)
409 {
410   if (serial1Buffered) {
411     while (transmit1IsBusy)
412       ;
413     transmit1IsBusy=1;
414     SBUF1=c;
415   } else {
416     while (!TI_1)
417       ;
418     SBUF1=c;
419     TI_1=0;
420   }
421 }
422
423 char Serial1GetChar (void)
424 {
425   char c;
426   if (serial1Buffered) {
427     while (receive1BufferHead==receive1BufferTail)
428       ;
429     c=receive1Buffer[receive1BufferTail];
430     ES1=0; // disable serial interrupts
431     receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
432     ES1=1; // enable serial interrupts
433   } else {
434     while (!RI_1)
435       ;
436     c=SBUF1;
437     RI_1=0;
438   }
439   return c;
440 }
441
442 void Serial1SendBreak() {
443   P5 &= ~0x08;
444   ClockMilliSecondsDelay(2);
445   P5 |= 0x08;
446 }
447
448 void Serial1Flush() {
449   ES1=0; // disable interrupts
450   receive1BufferHead=receive1BufferTail=0;
451   RI_1=0;
452   if (serial1Buffered) {
453     TI_1=0;
454     ES1=1; // enable interrupts
455   } else {
456     TI_1=1;
457   }
458 }
459
460 // now let's go for the clock stuff
461
462 // these REALLY need to be in data space for the irq routine!
463 static data unsigned long milliSeconds=0;
464 static data unsigned int timer0ReloadValue;
465
466 void ClockInit() {
467   unsigned long timerReloadValue=OSCILLATOR/1000;
468
469   switch (cpuSpeed) {
470   case 4: timerReloadValue/=4; break;
471   case 1: // not tested yet
472   case 2:  // not tested yet
473   default: timerReloadValue/=2; break;
474   }
475   timer0ReloadValue=~timerReloadValue;
476   // initialise timer 0
477   ET0=0; // disable timer interrupts initially
478   TCON = (TCON&0xcc)|0x00; // stop timer, clear overflow
479   TMOD = (TMOD&0xf0)|0x01; // 16 bit counter
480   CKCON|=0x08; // timer uses xtal/4
481   
482   TL0=timer0ReloadValue&0xff;
483   TH0=timer0ReloadValue>>8;
484   
485   ET0=1; // enable timer interrupts
486   TR0=1; // start timer
487 }
488
489 // This needs to be SUPER fast. What we really want is:
490
491 #if 0
492 void junk_ClockIrqHandler (void) interrupt 10 {
493   TL0=timer0ReloadValue&0xff;
494   TH0=timer0ReloadValue>>8;
495   milliSeconds++;
496 }
497 #else
498 // but look at the code, and the pushes and pops, so:
499 void ClockIrqHandler (void) interrupt 1 _naked
500 {
501   _asm
502     push acc
503     push psw
504     mov _TL0,_timer0ReloadValue
505     mov _TH0,_timer0ReloadValue+1
506     clr a
507     inc _milliSeconds+0
508     cjne a,_milliSeconds+0,_ClockIrqHandlerDone
509     inc _milliSeconds+1
510     cjne a,_milliSeconds+1,_ClockIrqHandlerDone
511     inc _milliSeconds+2
512     cjne a,_milliSeconds+2,_ClockIrqHandlerDone
513     inc _milliSeconds+3
514    _ClockIrqHandlerDone:
515     pop psw
516     pop acc
517     reti
518   _endasm;
519 }
520 #endif
521
522 // we can't just use milliSeconds
523 unsigned long ClockTicks(void) {
524   unsigned long ms;
525   ET0=0;
526   ms=milliSeconds;
527   ET0=1;
528   return ms;
529 }
530
531 void ClockMilliSecondsDelay(unsigned long delay) {
532   long ms=ClockTicks()+delay;
533
534   while (ms>ClockTicks())
535     ;
536 }
537
538 // stolen from Kevin Vigor, works only for TINI at default speed
539 void ClockMicroSecondsDelay(unsigned int delay) {
540    delay; /* shut compiler up. */
541    
542    _asm
543      
544    ; delay is in dpl/dph
545    mov  r0, dpl
546    mov  r1, dph
547    
548    mov  a, r0
549    orl  a, r1                   ; quick out for zero case.
550    jz   _usDelayDone
551    
552    inc  r1
553    cjne r0, #0, _usDelayLoop
554    dec  r1
555    
556    _usDelayLoop:
557    nop
558    nop
559    nop
560    nop
561    nop
562    nop
563    nop                          ; 7 nops
564    djnz r0, _usDelayLoop        ; 3 cycles x 1 = 3 cycles
565                                 ; 10 cycles per iter
566                                 ; we want 9.216, but more is better
567                                 ; than less.
568    djnz r1, _usDelayLoop        
569 _usDelayDone:
570    
571    _endasm;
572   
573 }