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