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) {
137 while (0 && (EXIF&0x04))
138 ; // cpu operates from ring buffer
139 PMR = 0x80; // div4, CTM off, multiplier 2x
143 PMR=0x88; // div4, CTM off, multiplier 4x
144 PMR=0x98; // div4, CTM on, multiplier 4x
145 while ((EXIF&0x08)==0) {
146 ; // wait for the multiplier to be ready
148 PMR = 0x18; // use multiplier
152 PMR=0x90; // div4, CTM on, multilier 2x
153 while ((EXIF&0x08)==0) {
154 ; // wait for the multiplier to be ready
156 PMR = 0x10; // use multiplier
164 PMR = 0xc0; // div1024, CTM off
170 // now the serial0 stuff
172 // just to make the code more readable
173 #define S0RBS SERIAL_0_RECEIVE_BUFFER_SIZE
175 // this is a ring buffer and can overflow at anytime!
176 static volatile unsigned char receive0Buffer[S0RBS];
177 static volatile int receive0BufferHead=0;
178 static volatile int receive0BufferTail=0;
179 // no buffering for transmit
180 static volatile char transmit0IsBusy=0;
182 static data unsigned char serial0Buffered;
184 /* Initialize serial0.
186 Available baudrates are from 110 upto 115200 (using 16-bit timer 2)
187 If baud==0, the port is disabled.
189 If buffered!=0, characters received are buffered using an interrupt
192 void Serial0Init (unsigned long baud, unsigned char buffered) {
195 ES0=0; // disable interrupts
196 SCON0 &= 0xef; // disable receiver
200 ES0 = 0; // disable serial channel 0 interrupt
201 TR2 = 0; // stop timer 2
203 // set 8 bit uart with variable baud from timer 1/2
204 // enable receiver and clear RI and TI
207 PCON |= 0x80; // clock is 16x bitrate
208 CKCON|=0x20; // timer uses xtal/4
210 T2MOD=0; // no fancy functions
211 T2CON=0x34; // start timer as a baudrate generator for serial0
216 serial0Buffered=buffered;
219 RI_0=TI_0=0; // clear "pending" interrupts
220 ES0 = 1; // enable serial channel 0 interrupt
222 RI_0=0; // receive buffer empty
223 TI_0=1; // transmit buffer empty
227 void Serial0Baud(unsigned long baud) {
229 baud=-((long)OSCILLATOR/(32*baud));
232 TF2=0; // clear overflow flag
233 TR2=1; // start timer
236 void Serial0IrqHandler (void) interrupt 4 {
238 receive0Buffer[receive0BufferHead]=SBUF0;
239 receive0BufferHead=(receive0BufferHead+1)&(S0RBS-1);
240 if (receive0BufferHead==receive0BufferTail) {
241 /* buffer overrun, sorry :) */
242 receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
252 char Serial0CharArrived(void) {
253 if (serial0Buffered) {
254 if (receive0BufferHead!=receive0BufferTail)
255 return receive0Buffer[receive0BufferTail];
263 void Serial0PutChar (char c)
265 if (serial0Buffered) {
266 while (transmit0IsBusy)
278 char Serial0GetChar (void)
281 if (serial0Buffered) {
282 while (receive0BufferHead==receive0BufferTail)
284 c=receive0Buffer[receive0BufferTail];
285 ES0=0; // disable serial interrupts
286 receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
287 ES0=1; // enable serial interrupts
297 void Serial0SendBreak() {
299 ClockMilliSecondsDelay(2);
303 void Serial0Flush() {
304 ES0=0; // disable interrupts
305 receive0BufferHead=receive0BufferTail=0;
307 if (serial0Buffered) {
309 ES0=1; // enable interrupts
315 /* now let's go for the serial1 stuff, basically it's a replicate of
316 serial0 except it uses timer 1
319 // just to make the code more readable
320 #define S1RBS SERIAL_1_RECEIVE_BUFFER_SIZE
322 // this is a ring buffer and can overflow at anytime!
323 static volatile unsigned char receive1Buffer[S1RBS];
324 static volatile int receive1BufferHead=0;
325 static volatile int receive1BufferTail=0;
326 // no buffering for transmit
327 static volatile char transmit1IsBusy=0;
329 static data unsigned char serial1Buffered;
331 /* Initialize serial1.
333 Available baudrates are from 4800 upto 115200 (using 8-bit timer 1)
334 If baud==0, the port is disabled.
336 If buffered!=0, characters received are buffered using an interrupt
339 void Serial1Init (unsigned long baud, unsigned char buffered) {
342 ES1=0; // disable interrupt
343 SCON1 &= 0xef; // disable receiver
344 return; // and don't touch it
347 ES1 = 0; // disable channel 1 interrupt
348 TR1 = 0; // stop timer 1
350 // set 8 bit uart with variable baud from timer 1
351 // enable receiver and clear RI and TI
354 WDCON |= 0x80; // clock is 16x bitrate
355 CKCON|=0x10; // timer uses xtal/4
357 TMOD = (TMOD&0x0f) | 0x20; // timer 1 is an 8bit auto-reload counter
362 serial1Buffered=buffered;
365 RI_1=TI_1=0; // clear "pending" interrupts
366 ES1 = 1; // enable serial channel 1 interrupt
368 RI_1=0; // receive buffer empty
369 TI_1=1; // transmit buffer empty
373 void Serial1Baud(unsigned long baud) {
375 baud=-((long)OSCILLATOR/(32*baud));
377 TF1=0; // clear overflow flag
378 TR1=1; // start timer
381 void Serial1IrqHandler (void) interrupt 7 {
383 receive1Buffer[receive1BufferHead]=SBUF1;
384 receive1BufferHead=(receive1BufferHead+1)&(S1RBS-1);
385 if (receive1BufferHead==receive1BufferTail) /* buffer overrun, sorry :) */
386 receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
395 char Serial1CharArrived(void) {
396 if (serial1Buffered) {
397 if (receive1BufferHead!=receive1BufferTail)
398 return receive1Buffer[receive1BufferTail];
406 void Serial1PutChar (char c)
408 if (serial1Buffered) {
409 while (transmit1IsBusy)
421 char Serial1GetChar (void)
424 if (serial1Buffered) {
425 while (receive1BufferHead==receive1BufferTail)
427 c=receive1Buffer[receive1BufferTail];
428 ES1=0; // disable serial interrupts
429 receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
430 ES1=1; // enable serial interrupts
440 void Serial1SendBreak() {
442 ClockMilliSecondsDelay(2);
446 void Serial1Flush() {
447 ES1=0; // disable interrupts
448 receive1BufferHead=receive1BufferTail=0;
450 if (serial1Buffered) {
452 ES1=1; // enable interrupts
458 // now let's go for the clock stuff
460 // these REALLY need to be in data space for the irq routine!
461 static data unsigned long milliSeconds=0;
462 static data unsigned int timer0ReloadValue;
465 unsigned long timerReloadValue=OSCILLATOR/1000;
468 case 4: timerReloadValue/=4; break;
469 case 1: // not tested yet
470 case 2: // not tested yet
471 default: timerReloadValue/=2; break;
473 timer0ReloadValue=~timerReloadValue;
474 // initialise timer 0
475 ET0=0; // disable timer interrupts initially
476 TCON = (TCON&0xcc)|0x00; // stop timer, clear overflow
477 TMOD = (TMOD&0xf0)|0x01; // 16 bit counter
478 CKCON|=0x08; // timer uses xtal/4
480 TL0=timer0ReloadValue&0xff;
481 TH0=timer0ReloadValue>>8;
483 ET0=1; // enable timer interrupts
484 TR0=1; // start timer
487 // This needs to be SUPER fast. What we really want is:
490 void junk_ClockIrqHandler (void) interrupt 10 {
491 TL0=timer0ReloadValue&0xff;
492 TH0=timer0ReloadValue>>8;
496 // but look at the code, and the pushes and pops, so:
497 void ClockIrqHandler (void) interrupt 1 _naked
503 mov _TL0,_timer0ReloadValue
504 mov _TH0,_timer0ReloadValue+1
507 cjne a,_milliSeconds+0,_ClockIrqHandlerDone
509 cjne a,_milliSeconds+1,_ClockIrqHandlerDone
511 cjne a,_milliSeconds+2,_ClockIrqHandlerDone
513 _ClockIrqHandlerDone:
521 // we can't just use milliSeconds
522 unsigned long ClockTicks(void) {
530 void ClockMilliSecondsDelay(unsigned long delay) {
531 long ms=ClockTicks()+delay;
533 while (ms>ClockTicks())
537 // stolen from Kevin Vigor, works only for TINI at default speed
538 void ClockMicroSecondsDelay(unsigned int delay) {
539 delay; /* shut compiler up. */
543 ; delay is in dpl/dph
548 orl a, r1 ; quick out for zero case.
552 cjne r0, #0, _usDelayLoop
563 djnz r0, _usDelayLoop ; 3 cycles x 1 = 3 cycles
565 ; we want 9.216, but more is better
567 djnz r1, _usDelayLoop