Announce SDCC 4.5.0 RC1.
[sdcc.git] / sdcc / device / lib / ds390 / tinibios.c
blob6288d9f2f861e550dfce7d553c6e867b4c10c42b
1 /*-------------------------------------------------------------------------
2 tinibios.c - startup and serial routines for the DS80C390 (tested on TINI)
4 Copyright (C) 2001, Johan Knol <johan.knol AT iduna.nl>
6 This library 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.
11 This library 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.
16 You should have received a copy of the GNU General Public License
17 along with this library; see the file COPYING. If not, write to the
18 Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
19 MA 02110-1301, USA.
21 As a special exception, if you link this library with other files,
22 some of which are compiled with SDCC, to produce an executable,
23 this library does not by itself cause the resulting executable to
24 be covered by the GNU General Public License. This exception does
25 not however invalidate any other reasons why the executable file
26 might be covered by the GNU General Public License.
27 -------------------------------------------------------------------------*/
29 #include <tinibios.h>
30 #include <stdio.h>
32 #define TIMED_ACCESS(sfr,value) { TA=0xaa; TA=0x55; sfr=value; }
34 unsigned char __sdcc_external_startup(void)
36 IE=0; // disable ALL interrupts
38 // use A19..16 and !CE3..0, no CAN
39 TIMED_ACCESS(P4CNT,0x3f);
41 // use !PCE3..0, serial 1 at P5.2/3
42 TIMED_ACCESS(P5CNT,0x27);
44 // disable watchdog
45 EWT=0;
47 // watchdog set to 9.1 seconds
48 // CKCON|=0xc0;
50 // default stretch cycles for MOVX
51 //CKCON = (CKCON&0xf8)|(CPU_MOVX_STRETCH&0x07);
52 CKCON=0xf9;
54 // use internal 4k RAM as data(stack) memory at 0x400000 and
55 // move CANx memory access to 0x401000 and upwards
56 // use !CE* for program and/or data memory access
57 TIMED_ACCESS(MCON,0xaf);
59 // select default cpu speed
60 CpuSpeed(CPU_SPEED);
62 // Copy the Interrupt Vector Table (128 bytes) from 0x10000 to 0x100000
63 // This isn't needed for older bootloaders than the 0515, but it won't harm
64 __asm
65 push dpx
66 push dph
67 push dpl
68 push dps
69 push b
70 push acc
71 mov dps,#0x00 ; make sure no autoincrement in progress
72 mov dptr,#0x10000 ; from
73 inc dps ; switch to alternate dptr
74 mov dptr,#0x100000 ; to
75 mov b,#0x80 ; count
77 _Startup390CopyIVT:
78 inc dps
79 movx a,@dptr
80 inc dptr
81 inc dps
82 movx @dptr,a
83 inc dptr
84 djnz b,_Startup390CopyIVT
86 pop acc
87 pop b
88 pop dps
89 pop dpl
90 pop dph
91 pop dpx
92 __endasm;
94 // global interrupt enable, all masks cleared
95 // let the Gods be with us :)
96 IE = 0x80;
98 Serial0Init(SERIAL_0_BAUD,1);
99 //Serial1Init(SERIAL_1_BAUD,1);
100 ClockInit();
101 //RtcInit();
102 //WatchDogInit();
104 // signal _sdcc_gsinit_startup to initialize data (call _sdcc_init_data)
105 return 0;
108 /* Set the cpu speed in clocks per machine cycle, valid values are:
109 1024: Power management mode
110 4: Divide-by-4 mode
111 2: Use frequency multiplier (2x)
112 1: Use frequency multiplier (4x) (Don't do this on a TINI at 18.432MHz)
114 TODO: TINI seems to support only 2 and 4: write only bits in PMR ?
117 unsigned int cpuSpeed;
119 void CpuSpeed(unsigned int speed)
121 #if 0
122 while (0 && (EXIF&0x04))
123 ; // cpu operates from ring buffer
124 #endif
125 PMR = 0x80; // div4, CTM off, multiplier 2x
126 switch (speed)
128 case 1:
129 PMR=0x88; // div4, CTM off, multiplier 4x
130 PMR=0x98; // div4, CTM on, multiplier 4x
131 while ((EXIF&0x08)==0) {
132 ; // wait for the multiplier to be ready
134 PMR = 0x18; // use multiplier
135 cpuSpeed=speed;
136 break;
137 case 2:
138 PMR=0x90; // div4, CTM on, multilier 2x
139 while ((EXIF&0x08)==0) {
140 ; // wait for the multiplier to be ready
142 PMR = 0x10; // use multiplier
143 cpuSpeed=speed;
144 break;
145 case 4:
146 // nothing to do
147 cpuSpeed=speed;
148 break;
149 case 1024:
150 PMR = 0xc0; // div1024, CTM off
151 cpuSpeed=speed;
152 break;
156 // now the serial0 stuff
158 // just to make the code more readable
159 #define S0RBS SERIAL_0_RECEIVE_BUFFER_SIZE
161 // this is a ring buffer and can overflow at anytime!
162 static volatile unsigned char receive0Buffer[S0RBS];
163 static volatile int receive0BufferHead=0;
164 static volatile int receive0BufferTail=0;
165 // no buffering for transmit
166 static volatile char transmit0IsBusy=0;
168 static __data unsigned char serial0Buffered;
170 /* Initialize serial0.
172 Available baudrates are from 110 upto 115200 (using 16-bit timer 2)
173 If baud==0, the port is disabled.
175 If buffered!=0, characters received are buffered using an interrupt
178 void Serial0Init (unsigned long baud, unsigned char buffered)
180 if (baud==0) {
181 ES0=0; // disable interrupts
182 SCON0 &= 0xef; // disable receiver
183 return;
186 ES0 = 0; // disable serial channel 0 interrupt
187 TR2 = 0; // stop timer 2
189 // set 8 bit uart with variable baud from timer 1/2
190 // enable receiver and clear RI and TI
191 SCON0 = 0x50;
193 PCON |= 0x80; // clock is 16x bitrate
194 CKCON|=0x20; // timer uses xtal/4
196 T2MOD=0; // no fancy functions
197 T2CON=0x34; // start timer as a baudrate generator for serial0
199 // set the baud rate
200 Serial0Baud(baud);
202 serial0Buffered=buffered;
204 if (buffered) {
205 RI_0=TI_0=0; // clear "pending" interrupts
206 ES0 = 1; // enable serial channel 0 interrupt
207 } else {
208 RI_0=0; // receive buffer empty
209 TI_0=1; // transmit buffer empty
213 void Serial0Baud(unsigned long baud)
215 TR2=0; // stop timer
216 baud=-((long)OSCILLATOR/(32*baud));
217 TL2=RCAP2L= baud;
218 TH2=RCAP2H= baud>>8;
219 TF2=0; // clear overflow flag
220 TR2=1; // start timer
223 void Serial0IrqHandler (void) __interrupt (4)
225 if (RI_0) {
226 receive0Buffer[receive0BufferHead]=SBUF0;
227 receive0BufferHead=(receive0BufferHead+1)&(S0RBS-1);
228 if (receive0BufferHead==receive0BufferTail) {
229 /* buffer overrun, sorry :) */
230 receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
232 RI_0=0;
234 if (TI_0) {
235 TI_0=0;
236 transmit0IsBusy=0;
240 char Serial0CharArrived(void)
242 if (serial0Buffered) {
243 if (receive0BufferHead!=receive0BufferTail)
244 return receive0Buffer[receive0BufferTail];
245 } else {
246 if (RI_0)
247 return SBUF0;
249 return 0;
252 void Serial0PutChar (char c)
254 if (serial0Buffered) {
255 while (transmit0IsBusy)
257 transmit0IsBusy=1;
258 SBUF0=c;
259 } else {
260 while (!TI_0)
262 SBUF0=c;
263 TI_0=0;
267 char Serial0GetChar (void)
269 char c;
270 if (serial0Buffered) {
271 while (receive0BufferHead==receive0BufferTail)
273 c=receive0Buffer[receive0BufferTail];
274 ES0=0; // disable serial interrupts
275 receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
276 ES0=1; // enable serial interrupts
277 } else {
278 while (!RI_0)
280 c=SBUF0;
281 RI_0=0;
283 return c;
286 void Serial0SendBreak (void)
288 P3 &= ~0x02;
289 ClockMilliSecondsDelay(2);
290 P3 |= 0x02;
293 void Serial0Flush (void)
295 ES0=0; // disable interrupts
296 receive0BufferHead=receive0BufferTail=0;
297 RI_0=0;
298 if (serial0Buffered) {
299 TI_0=0;
300 ES0=1; // enable interrupts
301 } else {
302 TI_0=1;
306 /* now let's go for the serial1 stuff, basically it's a replicate of
307 serial0 except it uses timer 1
310 // just to make the code more readable
311 #define S1RBS SERIAL_1_RECEIVE_BUFFER_SIZE
313 // this is a ring buffer and can overflow at anytime!
314 static volatile unsigned char receive1Buffer[S1RBS];
315 static volatile int receive1BufferHead=0;
316 static volatile int receive1BufferTail=0;
317 // no buffering for transmit
318 static volatile char transmit1IsBusy=0;
320 static __data unsigned char serial1Buffered;
322 /* Initialize serial1.
324 Available baudrates are from 4800 upto 115200 (using 8-bit timer 1)
325 If baud==0, the port is disabled.
327 If buffered!=0, characters received are buffered using an interrupt
330 void Serial1Init (unsigned long baud, unsigned char buffered)
332 if (baud==0) {
333 ES1=0; // disable interrupt
334 SCON1 &= 0xef; // disable receiver
335 return; // and don't touch it
338 ES1 = 0; // disable channel 1 interrupt
339 TR1 = 0; // stop timer 1
341 // set 8 bit uart with variable baud from timer 1
342 // enable receiver and clear RI and TI
343 SCON1 = 0x50;
345 WDCON |= 0x80; // clock is 16x bitrate
346 CKCON|=0x10; // timer uses xtal/4
348 TMOD = (TMOD&0x0f) | 0x20; // timer 1 is an 8bit auto-reload counter
350 // set the baud rate
351 Serial1Baud(baud);
353 serial1Buffered=buffered;
355 if (buffered) {
356 RI_1=TI_1=0; // clear "pending" interrupts
357 ES1 = 1; // enable serial channel 1 interrupt
358 } else {
359 RI_1=0; // receive buffer empty
360 TI_1=1; // transmit buffer empty
364 void Serial1Baud(unsigned long baud)
366 TR1=0; // stop timer
367 baud=-((long)OSCILLATOR/(32*baud));
368 TL1=TH1 = baud;
369 TF1=0; // clear overflow flag
370 TR1=1; // start timer
373 void Serial1IrqHandler (void) __interrupt (7)
375 if (RI_1) {
376 receive1Buffer[receive1BufferHead]=SBUF1;
377 receive1BufferHead=(receive1BufferHead+1)&(S1RBS-1);
378 if (receive1BufferHead==receive1BufferTail) /* buffer overrun, sorry :) */
379 receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
380 RI_1=0;
382 if (TI_1) {
383 TI_1=0;
384 transmit1IsBusy=0;
388 char Serial1CharArrived(void)
390 if (serial1Buffered) {
391 if (receive1BufferHead!=receive1BufferTail)
392 return receive1Buffer[receive1BufferTail];
393 } else {
394 if (RI_1)
395 return SBUF1;
397 return 0;
400 void Serial1PutChar (char c)
402 if (serial1Buffered) {
403 while (transmit1IsBusy)
405 transmit1IsBusy=1;
406 SBUF1=c;
407 } else {
408 while (!TI_1)
410 SBUF1=c;
411 TI_1=0;
415 char Serial1GetChar (void)
417 char c;
418 if (serial1Buffered) {
419 while (receive1BufferHead==receive1BufferTail)
421 c=receive1Buffer[receive1BufferTail];
422 ES1=0; // disable serial interrupts
423 receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
424 ES1=1; // enable serial interrupts
425 } else {
426 while (!RI_1)
428 c=SBUF1;
429 RI_1=0;
431 return c;
434 void Serial1SendBreak (void)
436 P5 &= ~0x08;
437 ClockMilliSecondsDelay(2);
438 P5 |= 0x08;
441 void Serial1Flush (void)
443 ES1=0; // disable interrupts
444 receive1BufferHead=receive1BufferTail=0;
445 RI_1=0;
446 if (serial1Buffered) {
447 TI_1=0;
448 ES1=1; // enable interrupts
449 } else {
450 TI_1=1;
454 // now let's go for the clock stuff
456 // these REALLY need to be in data space for the irq routine!
457 static __data unsigned long milliSeconds=0;
458 static __data unsigned int timer0ReloadValue;
460 void ClockInit (void)
462 unsigned long timerReloadValue=OSCILLATOR/1000;
464 switch (cpuSpeed) {
465 case 4: timerReloadValue/=4; break;
466 case 1: // not tested yet
467 case 2: // not tested yet
468 default: timerReloadValue/=2; break;
470 timer0ReloadValue=~timerReloadValue;
471 // initialise timer 0
472 ET0=0; // disable timer interrupts initially
473 TCON = (TCON&0xcc)|0x00; // stop timer, clear overflow
474 TMOD = (TMOD&0xf0)|0x01; // 16 bit counter
475 CKCON|=0x08; // timer uses xtal/4
477 TL0=timer0ReloadValue&0xff;
478 TH0=timer0ReloadValue>>8;
480 ET0=1; // enable timer interrupts
481 TR0=1; // start timer
484 // This needs to be SUPER fast. What we really want is:
486 #if 0
487 void junk_ClockIrqHandler (void) __interrupt (10)
489 TL0 = timer0ReloadValue & 0xff;
490 TH0 = timer0ReloadValue >> 8;
491 milliSeconds++;
493 #else
494 // but look at the code, and the pushes and pops, so:
495 void ClockIrqHandler (void) __interrupt (1) __naked
497 __asm
498 push acc
499 push psw
500 mov _TL0,_timer0ReloadValue
501 mov _TH0,_timer0ReloadValue+1
502 clr a
503 inc _milliSeconds+0
504 cjne a,_milliSeconds+0,_ClockIrqHandlerDone
505 inc _milliSeconds+1
506 cjne a,_milliSeconds+1,_ClockIrqHandlerDone
507 inc _milliSeconds+2
508 cjne a,_milliSeconds+2,_ClockIrqHandlerDone
509 inc _milliSeconds+3
510 _ClockIrqHandlerDone:
511 pop psw
512 pop acc
513 reti
514 __endasm;
516 #endif
518 // we can't just use milliSeconds
519 unsigned long ClockTicks(void)
521 unsigned long ms;
522 ET0=0;
523 ms=milliSeconds;
524 ET0=1;
525 return ms;
528 void ClockMilliSecondsDelay(unsigned long delay)
530 long ms=ClockTicks()+delay;
532 while (ms>ClockTicks())
536 // stolen from Kevin Vigor, works only for TINI at default speed
537 void ClockMicroSecondsDelay(unsigned int delay)
539 delay; /* shut compiler up. */
541 __asm
543 ; delay is in dpl/dph
544 mov r0, dpl
545 mov r1, dph
547 mov a, r0
548 orl a, r1 ; quick out for zero case.
549 jz _usDelayDone
551 inc r1
552 cjne r0, #0, _usDelayLoop
553 dec r1
555 _usDelayLoop:
562 nop ; 7 nops
563 djnz r0, _usDelayLoop ; 3 cycles x 1 = 3 cycles
564 ; 10 cycles per iter
565 ; we want 9.216, but more is better
566 ; than less.
567 djnz r1, _usDelayLoop
568 _usDelayDone:
570 __endasm;