d70e4427cfbe8874f62a76526def2da11f9d46c6
[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   }
345
346   ES1 = 0; // disable channel 1 interrupt
347   TR1 = 0; // stop timer 1
348   
349   // set 8 bit uart with variable baud from timer 1
350   // enable receiver and clear RI and TI
351   SCON1 = 0x50;
352   
353   WDCON |= 0x80; // clock is 16x bitrate
354   CKCON|=0x10; // timer uses xtal/4
355   
356   TMOD = (TMOD&0x0f) | 0x20; // timer 1 is an 8bit auto-reload counter
357   
358   // set the baud rate
359   Serial1Baud(baud);
360   
361   serial1Buffered=buffered;
362
363   if (buffered) {
364     RI_1=TI_1=0; // clear "pending" interrupts
365     ES1 = 1; // enable serial channel 1 interrupt
366   } else {
367     RI_1=0; // receive buffer empty
368     TI_1=1; // transmit buffer empty
369   }
370 }
371
372 void Serial1Baud(unsigned long baud) {
373   TR1=0; // stop timer
374   baud=-((long)OSCILLATOR/(32*baud));
375   TL1=TH1 = baud;
376   TF1=0; // clear overflow flag
377   TR1=1; // start timer
378 }  
379
380 void Serial1IrqHandler (void) interrupt 7 {
381   if (RI_1) {
382     receive1Buffer[receive1BufferHead]=SBUF1;
383     receive1BufferHead=(receive1BufferHead+1)&(S1RBS-1);
384     if (receive1BufferHead==receive1BufferTail) /* buffer overrun, sorry :) */
385       receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
386     RI_1=0;
387   }
388   if (TI_1) {
389     TI_1=0;
390     transmit1IsBusy=0;
391   }
392 }
393
394 char Serial1CharArrived(void) {
395   if (serial1Buffered) {
396     if (receive1BufferHead!=receive1BufferTail)
397       return receive1Buffer[receive1BufferTail];
398   } else {
399     if (RI_1)
400       return SBUF1;
401   }
402   return 0;
403 }
404
405 void Serial1PutChar (char c)
406 {
407   if (serial1Buffered) {
408     while (transmit1IsBusy)
409       ;
410     transmit1IsBusy=1;
411     SBUF1=c;
412   } else {
413     while (!TI_1)
414       ;
415     SBUF1=c;
416     TI_1=0;
417   }
418 }
419
420 char Serial1GetChar (void)
421 {
422   char c;
423   if (serial1Buffered) {
424     while (receive1BufferHead==receive1BufferTail)
425       ;
426     c=receive1Buffer[receive1BufferTail];
427     ES1=0; // disable serial interrupts
428     receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
429     ES1=1; // enable serial interrupts
430   } else {
431     while (!RI_1)
432       ;
433     c=SBUF1;
434     RI_1=0;
435   }
436   return c;
437 }
438
439 void Serial1SendBreak() {
440   P5 &= ~0x08;
441   ClockMilliSecondsDelay(2);
442   P5 |= 0x08;
443 }
444
445 void Serial1Flush() {
446   ES1=0; // disable interrupts
447   receive1BufferHead=receive1BufferTail=0;
448   RI_1=0;
449   if (serial1Buffered) {
450     TI_1=0;
451     ES1=1; // enable interrupts
452   } else {
453     TI_1=1;
454   }
455 }
456
457 // now let's go for the clock stuff
458
459 // these REALLY need to be in data space for the irq routine!
460 static data unsigned long milliSeconds=0;
461 static data unsigned int timer0ReloadValue;
462
463 void ClockInit() {
464   unsigned long timerReloadValue=OSCILLATOR/1000;
465
466   switch (cpuSpeed) {
467   case 4: timerReloadValue/=4; break;
468   case 1: // not tested yet
469   case 2:  // not tested yet
470   default: timerReloadValue/=2; break;
471   }
472   timer0ReloadValue=~timerReloadValue;
473   // initialise timer 0
474   ET0=0; // disable timer interrupts initially
475   TCON = (TCON&0xcc)|0x00; // stop timer, clear overflow
476   TMOD = (TMOD&0xf0)|0x01; // 16 bit counter
477   CKCON|=0x08; // timer uses xtal/4
478   
479   TL0=timer0ReloadValue&0xff;
480   TH0=timer0ReloadValue>>8;
481   
482   ET0=1; // enable timer interrupts
483   TR0=1; // start timer
484 }
485
486 // This needs to be SUPER fast. What we really want is:
487
488 #if 0
489 void ClockIrqHandler (void) interrupt 1 {
490   TL0=timer0ReloadValue&0xff;
491   TH0=timer0ReloadValue>>8;
492   milliSeconds++;
493 }
494 #else
495 // but look at the code, and the pushes and pops, so:
496 #pragma EXCLUDE b,dpl,dph,dpx
497 void ClockIrqHandler (void) interrupt 1 {
498   _asm
499     mov _TL0,_timer0ReloadValue
500     mov _TH0,_timer0ReloadValue+1
501     mov a,#0x01
502     add a,_milliSeconds+0
503     mov _milliSeconds,a
504     jnc _ClockIrqHandlerDone
505     clr a
506     addc a,_milliSeconds+1
507     mov _milliSeconds+1,a
508     jnc _ClockIrqHandlerDone
509     clr a
510     addc a,_milliSeconds+2
511     mov _milliSeconds+1,a
512     jnc _ClockIrqHandlerDone
513     clr a
514     addc a,_milliSeconds+3
515     mov _milliSeconds+1,a
516    _ClockIrqHandlerDone:
517   _endasm;
518 }
519 #pragma EXCLUDE NONE
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 }