initial import of tinibios
[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
49   // use internal 4k RAM as data(stack) memory at 0x400000 and
50   // move CANx memory access to 0x401000 and upwards
51   // use !CE* for program and/or data memory access
52   TIMED_ACCESS(MCON,0xaf);
53
54   // select default cpu speed
55   CpuSpeed(CPU_SPEED);
56
57   _asm
58     ; save the 24-bit return address
59     pop ar2; msb
60     pop ar1
61     pop ar0; lsb
62
63
64     mov _ESP,#0x00; reinitialize the stack
65     mov _SP,#0x00
66
67     mov _TA,#0xaa; timed access
68     mov _TA,#0x55
69     mov _ACON,#0x06; 24 bit addresses, 10 bit stack at 0x400000
70
71     ; restore the 24-bit return address
72     push ar0; lsb
73     push ar1
74     push ar2; msb
75   _endasm;
76
77   // Copy the Interrupt Vector Table (128 bytes) from 0x10000 to 0x100000
78   // This isn't needed for older bootloaders than the 0515, but it won't harm
79   _asm
80   push dpx
81   push dph
82   push dpl
83   push dps
84   push b
85   push acc
86   mov dps,#0x00 ; make sure no autoincrement in progress
87   mov dptr,#0x10000 ; from
88   inc dps ; switch to alternate dptr
89   mov dptr,#0x100000 ; to
90   mov b,#0x80 ; count
91
92 _Startup390CopyIVT:
93   inc dps
94   movx a,@dptr
95   inc dptr
96   inc dps
97   movx @dptr,a
98   inc dptr
99   djnz b,_Startup390CopyIVT
100
101   pop acc
102   pop b
103   pop dps
104   pop dpl
105   pop dph
106   pop dpx
107   _endasm;
108
109   // global interrupt enable, all masks cleared
110   // let the Gods be with us :)
111   IE = 0x80; 
112
113   Serial0Init(SERIAL_0_BAUD,1);
114   //Serial1Init(SERIAL_1_BAUD,1);
115   ClockInit();
116   //RtcInit();
117   //WatchDogInit();
118
119   // signal _sdcc_gsinit_startup to initialize data (call _sdcc_init_data)
120   return 0; 
121 }
122
123 /* Set the cpu speed in clocks per machine cycle, valid values are:
124    1024: Power management mode
125       4: Divide-by-4 mode
126       2: Use frequency multiplier (2x)
127       1: Use frequency multiplier (4x) (Don't do this on a TINI at 18.432MHz)
128
129    TODO: TINI seems to support only 2 and 4: write only bits in PMR ?
130 */
131
132 unsigned int cpuSpeed;
133
134 void CpuSpeed(unsigned int speed) {
135
136   while (0 && (EXIF&0x04))
137     ; // cpu operates from ring buffer
138   PMR = 0x80; // div4, CTM off, multiplier 2x
139   switch (speed) 
140     {
141     case 1:
142       PMR=0x88; // div4, CTM off, multiplier 4x
143       PMR=0x98; // div4, CTM on, multiplier 4x
144       while ((EXIF&0x08)==0) {
145         ; // wait for the multiplier to be ready
146       }
147       PMR = 0x18; // use multiplier
148       cpuSpeed=speed;
149       break;
150     case 2:
151       PMR=0x90; // div4, CTM on, multilier 2x
152       while ((EXIF&0x08)==0) {
153         ; // wait for the multiplier to be ready
154       }
155       PMR = 0x10; // use multiplier
156       cpuSpeed=speed;
157       break;
158     case 4:
159       // nothing to do
160       cpuSpeed=speed;
161       break;
162     case 1024:
163       PMR = 0xc0; // div1024, CTM off
164       cpuSpeed=speed;
165       break;
166     }
167 }  
168
169 // now the serial0 stuff
170
171 // just to make the code more readable 
172 #define S0RBS SERIAL_0_RECEIVE_BUFFER_SIZE
173
174 // this is a ring buffer and can overflow at anytime!
175 static volatile unsigned char receive0Buffer[S0RBS];
176 static volatile int receive0BufferHead=0;
177 static volatile int receive0BufferTail=0;
178 // no buffering for transmit
179 static volatile char transmit0IsBusy=0;
180
181 static data unsigned char serial0Buffered;
182
183 /* Initialize serial0.
184
185    Available baudrates are from 110 upto 115200 (using 16-bit timer 2)
186    If baud==0, the port is disabled.
187
188    If buffered!=0, characters received are buffered using an interrupt
189 */
190
191 void Serial0Init (unsigned long baud, unsigned char buffered) {
192   
193   if (baud==0) {
194     ES0=0; // disable interrupts
195     SCON0 &= 0xef; // disable receiver
196     return;
197   }
198
199   ES0 = 0; // disable serial channel 0 interrupt
200   TR2 = 0; // stop timer 2
201   
202   // set 8 bit uart with variable baud from timer 1/2
203   // enable receiver and clear RI and TI
204   SCON0 = 0x50;
205   
206   PCON |= 0x80; // clock is 16x bitrate
207   CKCON|=0x20; // timer uses xtal/4
208   
209   T2MOD=0; // no fancy functions
210   T2CON=0x34; // start timer as a baudrate generator for serial0
211   
212   // set the baud rate
213   Serial0Baud(baud);
214   
215   serial0Buffered=buffered;
216  
217  if (buffered) {
218     RI_0=TI_0=0; // clear "pending" interrupts
219     ES0 = 1; // enable serial channel 0 interrupt
220   } else {
221     RI_0=0; // receive buffer empty
222     TI_0=1; // transmit buffer empty
223   }
224 }
225
226 void Serial0Baud(unsigned long baud) {
227   TR2=0; // stop timer
228   baud=-((long)OSCILLATOR/(32*baud));
229   TL2=RCAP2L= baud;
230   TH2=RCAP2H= baud>>8;
231   TF2=0; // clear overflow flag
232   TR2=1; // start timer
233 }  
234
235 void Serial0IrqHandler (void) interrupt 4 {
236   if (RI_0) {
237     receive0Buffer[receive0BufferHead]=SBUF0;
238     receive0BufferHead=(receive0BufferHead+1)&(S0RBS-1);
239     if (receive0BufferHead==receive0BufferTail) {
240       /* buffer overrun, sorry :) */
241       receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
242     }
243     RI_0=0;
244   }
245   if (TI_0) {
246     TI_0=0;
247     transmit0IsBusy=0;
248   }
249 }
250
251 char Serial0CharArrived(void) {
252   if (serial0Buffered) {
253     if (receive0BufferHead!=receive0BufferTail)
254       return receive0Buffer[receive0BufferTail];
255   } else {
256     if (RI_0)
257       return SBUF0;
258   }
259   return 0;
260 }
261
262 void Serial0PutChar (char c)
263 {
264   if (serial0Buffered) {
265     while (transmit0IsBusy)
266       ;
267     transmit0IsBusy=1;
268     SBUF0=c;
269   } else {
270     while (!TI_0)
271       ;
272     SBUF0=c;
273     TI_0=0;
274   }
275 }
276
277 char Serial0GetChar (void)
278 {
279   char c;
280   if (serial0Buffered) {
281     while (receive0BufferHead==receive0BufferTail)
282       ;
283     c=receive0Buffer[receive0BufferTail];
284     ES0=0; // disable serial interrupts
285     receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
286     ES0=1; // enable serial interrupts
287   } else {
288     while (!RI_0)
289       ;
290     c=SBUF0;
291     RI_0=0;
292   }
293   return c;
294 }
295
296 void Serial0SendBreak() {
297   P3 &= ~0x02;
298   ClockMilliSecondsDelay(2);
299   P3 |= 0x02;
300 }
301
302 void Serial0Flush() {
303   ES0=0; // disable interrupts
304   receive0BufferHead=receive0BufferTail=0;
305   RI_0=0;
306   if (serial0Buffered) {
307     TI_0=0;
308     ES0=1; // enable interrupts
309   } else {
310     TI_0=1;
311   }
312 }
313
314 /* now let's go for the serial1 stuff, basically it's a replicate of 
315    serial0 except it uses timer 1
316 */
317
318 // just to make the code more readable 
319 #define S1RBS SERIAL_1_RECEIVE_BUFFER_SIZE
320
321 // this is a ring buffer and can overflow at anytime!
322 static volatile unsigned char receive1Buffer[S1RBS];
323 static volatile int receive1BufferHead=0;
324 static volatile int receive1BufferTail=0;
325 // no buffering for transmit
326 static volatile char transmit1IsBusy=0;
327
328 static data unsigned char serial1Buffered;
329
330 /* Initialize serial1.
331
332    Available baudrates are from 4800 upto 115200 (using 8-bit timer 1)
333    If baud==0, the port is disabled.
334
335    If buffered!=0, characters received are buffered using an interrupt
336 */
337
338 void Serial1Init (unsigned long baud, unsigned char buffered) {
339   
340   if (baud=0) {
341     ES1=0; // disable interrupt
342     SCON1 &= 0xef; // disable receiver
343   }
344
345   ES1 = 0; // disable channel 1 interrupt
346   TR1 = 0; // stop timer 1
347   
348   // set 8 bit uart with variable baud from timer 1
349   // enable receiver and clear RI and TI
350   SCON1 = 0x50;
351   
352   WDCON |= 0x80; // clock is 16x bitrate
353   CKCON|=0x10; // timer uses xtal/4
354   
355   TMOD = (TMOD&0x0f) | 0x20; // timer 1 is an 8bit auto-reload counter
356   
357   // set the baud rate
358   Serial1Baud(baud);
359   
360   serial0Buffered=buffered;
361
362   if (buffered) {
363     RI_1=TI_1=0; // clear "pending" interrupts
364     ES1 = 1; // enable serial channel 1 interrupt
365   } else {
366     RI_1=0; // receive buffer empty
367     TI_1=1; // transmit buffer empty
368   }
369 }
370
371 void Serial1Baud(unsigned long baud) {
372   TR1=0; // stop timer
373   baud=-((long)OSCILLATOR/(32*baud));
374   TL1=TH1 = baud;
375   TF1=0; // clear overflow flag
376   TR1=1; // start timer
377 }  
378
379 void Serial1IrqHandler (void) interrupt 7 {
380   if (RI_1) {
381     receive1Buffer[receive1BufferHead]=SBUF1;
382     receive1BufferHead=(receive1BufferHead+1)&(S1RBS-1);
383     if (receive1BufferHead==receive1BufferTail) /* buffer overrun, sorry :) */
384       receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
385     RI_1=0;
386   }
387   if (TI_1) {
388     TI_1=0;
389     transmit1IsBusy=0;
390   }
391 }
392
393 char Serial1CharArrived(void) {
394   if (serial1Buffered) {
395     if (receive1BufferHead!=receive1BufferTail)
396       return receive1Buffer[receive1BufferTail];
397   } else {
398     if (RI_1)
399       return SBUF1;
400   }
401   return 0;
402 }
403
404 void Serial1PutChar (char c)
405 {
406   if (serial1Buffered) {
407     while (transmit1IsBusy)
408       ;
409     transmit1IsBusy=1;
410     SBUF1=c;
411   } else {
412     while (!TI_1)
413       ;
414     SBUF1=c;
415     TI_1=0;
416   }
417 }
418
419 char Serial1GetChar (void)
420 {
421   char c;
422   if (serial1Buffered) {
423     while (receive1BufferHead==receive1BufferTail)
424       ;
425     c=receive1Buffer[receive1BufferTail];
426     ES1=0; // disable serial interrupts
427     receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
428     ES1=1; // enable serial interrupts
429   } else {
430     while (!RI_1)
431       ;
432     c=SBUF1;
433     RI_1=0;
434   }
435   return c;
436 }
437
438 void Serial1SendBreak() {
439   P5 &= ~0x08;
440   ClockMilliSecondsDelay(2);
441   P5 |= 0x08;
442 }
443
444 void Serial1Flush() {
445   ES1=0; // disable interrupts
446   receive1BufferHead=receive1BufferTail=0;
447   RI_1=0;
448   if (serial1Buffered) {
449     TI_1=0;
450     ES1=1; // enable interrupts
451   } else {
452     TI_1=1;
453   }
454 }
455
456 // now let's go for the clock stuff
457
458 //#define TIMER_0_RELOAD_VALUE 18432000L/2/1000 // appr. 1ms
459
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|=0x80; // 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 void ClockIrqHandler (void) interrupt 1 {
487   // we have lost some time here
488   TL0=~(timer0ReloadValue&0xff);
489   TH0=~(timer0ReloadValue>>8);
490   milliSeconds++;
491   // that's all for now :)
492 }
493
494 // we can't just use milliSeconds
495 unsigned long ClockTicks(void) {
496   unsigned long ms;
497   ET0=0;
498   ms=milliSeconds;
499   ET0=1;
500   return ms;
501 }
502
503 void ClockMilliSecondsDelay(unsigned long delay) {
504   long ms=ClockTicks()+delay;
505
506   while (ms>ClockTicks())
507     ;
508 }
509
510 // stolen from Kevin Vigor, works only for TINI at default speed
511 void ClockMicroSecondsDelay(unsigned int delay) {
512    delay; /* shut compiler up. */
513    
514    _asm
515      
516    ; delay is in dpl/dph
517    mov  r0, dpl
518    mov  r1, dph
519    
520    mov  a, r0
521    orl  a, r1                   ; quick out for zero case.
522    jz   _usDelayDone
523    
524    inc  r1
525    cjne r0, #0, _usDelayLoop
526    dec  r1
527    
528    _usDelayLoop:
529    nop
530    nop
531    nop
532    nop
533    nop
534    nop
535    nop                          ; 7 nops
536    djnz r0, _usDelayLoop        ; 3 cycles x 1 = 3 cycles
537                                 ; 10 cycles per iter
538                                 ; we want 9.216, but more is better
539                                 ; than less.
540    djnz r1, _usDelayLoop        
541 _usDelayDone:
542    
543    _endasm;
544   
545 }