1 /*-------------------------------------------------------------------------
2 tinibios.c - startup and serial routines for the DS80C390 (tested on TINI)
4 Written By - Johan Knol, johan.knol@iduna.nl
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
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.
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.
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 -------------------------------------------------------------------------*/
28 #define TIMED_ACCESS(sfr,value) { TA=0xaa; TA=0x55; sfr=value; }
30 unsigned char _sdcc_external_startup(void)
32 IE=0; // disable ALL interrupts
34 // use A19..16 and !CE3..0, no CAN
35 TIMED_ACCESS(P4CNT,0x3f);
37 // use !PCE3..0, serial 1 at P5.2/3
38 TIMED_ACCESS(P5CNT,0x27);
43 // watchdog set to 9.1 seconds
46 // default stretch cycles for MOVX
47 //CKCON = (CKCON&0xf8)|(CPU_MOVX_STRETCH&0x07);
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);
55 // select default cpu speed
59 ; save the 24-bit return address
65 mov _TA,#0xaa; timed access
67 mov _ACON,#0x06; 24 bit addresses, 10 bit stack at 0x400000
69 mov _ESP,#0x00; reinitialize the stack
72 ; restore the 24-bit return address
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
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
100 djnz b,_Startup390CopyIVT
110 // global interrupt enable, all masks cleared
111 // let the Gods be with us :)
114 Serial0Init(SERIAL_0_BAUD,1);
115 //Serial1Init(SERIAL_1_BAUD,1);
120 // signal _sdcc_gsinit_startup to initialize data (call _sdcc_init_data)
124 /* Set the cpu speed in clocks per machine cycle, valid values are:
125 1024: Power management mode
127 2: Use frequency multiplier (2x)
128 1: Use frequency multiplier (4x) (Don't do this on a TINI at 18.432MHz)
130 TODO: TINI seems to support only 2 and 4: write only bits in PMR ?
133 unsigned int cpuSpeed;
135 void CpuSpeed(unsigned int speed)
138 while (0 && (EXIF&0x04))
139 ; // cpu operates from ring buffer
141 PMR = 0x80; // div4, CTM off, multiplier 2x
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
150 PMR = 0x18; // use multiplier
154 PMR=0x90; // div4, CTM on, multilier 2x
155 while ((EXIF&0x08)==0) {
156 ; // wait for the multiplier to be ready
158 PMR = 0x10; // use multiplier
166 PMR = 0xc0; // div1024, CTM off
172 // now the serial0 stuff
174 // just to make the code more readable
175 #define S0RBS SERIAL_0_RECEIVE_BUFFER_SIZE
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;
184 static __data unsigned char serial0Buffered;
186 /* Initialize serial0.
188 Available baudrates are from 110 upto 115200 (using 16-bit timer 2)
189 If baud==0, the port is disabled.
191 If buffered!=0, characters received are buffered using an interrupt
194 void Serial0Init (unsigned long baud, unsigned char buffered)
197 ES0=0; // disable interrupts
198 SCON0 &= 0xef; // disable receiver
202 ES0 = 0; // disable serial channel 0 interrupt
203 TR2 = 0; // stop timer 2
205 // set 8 bit uart with variable baud from timer 1/2
206 // enable receiver and clear RI and TI
209 PCON |= 0x80; // clock is 16x bitrate
210 CKCON|=0x20; // timer uses xtal/4
212 T2MOD=0; // no fancy functions
213 T2CON=0x34; // start timer as a baudrate generator for serial0
218 serial0Buffered=buffered;
221 RI_0=TI_0=0; // clear "pending" interrupts
222 ES0 = 1; // enable serial channel 0 interrupt
224 RI_0=0; // receive buffer empty
225 TI_0=1; // transmit buffer empty
229 void Serial0Baud(unsigned long baud)
232 baud=-((long)OSCILLATOR/(32*baud));
235 TF2=0; // clear overflow flag
236 TR2=1; // start timer
239 void Serial0IrqHandler (void) __interrupt 4
242 receive0Buffer[receive0BufferHead]=SBUF0;
243 receive0BufferHead=(receive0BufferHead+1)&(S0RBS-1);
244 if (receive0BufferHead==receive0BufferTail) {
245 /* buffer overrun, sorry :) */
246 receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
256 char Serial0CharArrived(void)
258 if (serial0Buffered) {
259 if (receive0BufferHead!=receive0BufferTail)
260 return receive0Buffer[receive0BufferTail];
268 void Serial0PutChar (char c)
270 if (serial0Buffered) {
271 while (transmit0IsBusy)
283 char Serial0GetChar (void)
286 if (serial0Buffered) {
287 while (receive0BufferHead==receive0BufferTail)
289 c=receive0Buffer[receive0BufferTail];
290 ES0=0; // disable serial interrupts
291 receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
292 ES0=1; // enable serial interrupts
302 void Serial0SendBreak()
305 ClockMilliSecondsDelay(2);
311 ES0=0; // disable interrupts
312 receive0BufferHead=receive0BufferTail=0;
314 if (serial0Buffered) {
316 ES0=1; // enable interrupts
322 /* now let's go for the serial1 stuff, basically it's a replicate of
323 serial0 except it uses timer 1
326 // just to make the code more readable
327 #define S1RBS SERIAL_1_RECEIVE_BUFFER_SIZE
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;
336 static __data unsigned char serial1Buffered;
338 /* Initialize serial1.
340 Available baudrates are from 4800 upto 115200 (using 8-bit timer 1)
341 If baud==0, the port is disabled.
343 If buffered!=0, characters received are buffered using an interrupt
346 void Serial1Init (unsigned long baud, unsigned char buffered)
349 ES1=0; // disable interrupt
350 SCON1 &= 0xef; // disable receiver
351 return; // and don't touch it
354 ES1 = 0; // disable channel 1 interrupt
355 TR1 = 0; // stop timer 1
357 // set 8 bit uart with variable baud from timer 1
358 // enable receiver and clear RI and TI
361 WDCON |= 0x80; // clock is 16x bitrate
362 CKCON|=0x10; // timer uses xtal/4
364 TMOD = (TMOD&0x0f) | 0x20; // timer 1 is an 8bit auto-reload counter
369 serial1Buffered=buffered;
372 RI_1=TI_1=0; // clear "pending" interrupts
373 ES1 = 1; // enable serial channel 1 interrupt
375 RI_1=0; // receive buffer empty
376 TI_1=1; // transmit buffer empty
380 void Serial1Baud(unsigned long baud)
383 baud=-((long)OSCILLATOR/(32*baud));
385 TF1=0; // clear overflow flag
386 TR1=1; // start timer
389 void Serial1IrqHandler (void) __interrupt 7
392 receive1Buffer[receive1BufferHead]=SBUF1;
393 receive1BufferHead=(receive1BufferHead+1)&(S1RBS-1);
394 if (receive1BufferHead==receive1BufferTail) /* buffer overrun, sorry :) */
395 receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
404 char Serial1CharArrived(void)
406 if (serial1Buffered) {
407 if (receive1BufferHead!=receive1BufferTail)
408 return receive1Buffer[receive1BufferTail];
416 void Serial1PutChar (char c)
418 if (serial1Buffered) {
419 while (transmit1IsBusy)
431 char Serial1GetChar (void)
434 if (serial1Buffered) {
435 while (receive1BufferHead==receive1BufferTail)
437 c=receive1Buffer[receive1BufferTail];
438 ES1=0; // disable serial interrupts
439 receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
440 ES1=1; // enable serial interrupts
450 void Serial1SendBreak()
453 ClockMilliSecondsDelay(2);
459 ES1=0; // disable interrupts
460 receive1BufferHead=receive1BufferTail=0;
462 if (serial1Buffered) {
464 ES1=1; // enable interrupts
470 // now let's go for the clock stuff
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;
478 unsigned long timerReloadValue=OSCILLATOR/1000;
481 case 4: timerReloadValue/=4; break;
482 case 1: // not tested yet
483 case 2: // not tested yet
484 default: timerReloadValue/=2; break;
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
493 TL0=timer0ReloadValue&0xff;
494 TH0=timer0ReloadValue>>8;
496 ET0=1; // enable timer interrupts
497 TR0=1; // start timer
500 // This needs to be SUPER fast. What we really want is:
503 void junk_ClockIrqHandler (void) __interrupt 10
505 TL0=timer0ReloadValue&0xff;
506 TH0=timer0ReloadValue>>8;
510 // but look at the code, and the pushes and pops, so:
511 void ClockIrqHandler (void) __interrupt 1 __naked
516 mov _TL0,_timer0ReloadValue
517 mov _TH0,_timer0ReloadValue+1
520 cjne a,_milliSeconds+0,_ClockIrqHandlerDone
522 cjne a,_milliSeconds+1,_ClockIrqHandlerDone
524 cjne a,_milliSeconds+2,_ClockIrqHandlerDone
526 _ClockIrqHandlerDone:
534 // we can't just use milliSeconds
535 unsigned long ClockTicks(void)
544 void ClockMilliSecondsDelay(unsigned long delay)
546 long ms=ClockTicks()+delay;
548 while (ms>ClockTicks())
552 // stolen from Kevin Vigor, works only for TINI at default speed
553 void ClockMicroSecondsDelay(unsigned int delay)
555 delay; /* shut compiler up. */
559 ; delay is in dpl/dph
564 orl a, r1 ; quick out for zero case.
568 cjne r0, #0, _usDelayLoop
579 djnz r0, _usDelayLoop ; 3 cycles x 1 = 3 cycles
581 ; we want 9.216, but more is better
583 djnz r1, _usDelayLoop