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