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) {
231 baud=-((long)OSCILLATOR/(32*baud));
234 TF2=0; // clear overflow flag
235 TR2=1; // start timer
238 void Serial0IrqHandler (void) interrupt 4 {
240 receive0Buffer[receive0BufferHead]=SBUF0;
241 receive0BufferHead=(receive0BufferHead+1)&(S0RBS-1);
242 if (receive0BufferHead==receive0BufferTail) {
243 /* buffer overrun, sorry :) */
244 receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
254 char Serial0CharArrived(void) {
255 if (serial0Buffered) {
256 if (receive0BufferHead!=receive0BufferTail)
257 return receive0Buffer[receive0BufferTail];
265 void Serial0PutChar (char c)
267 if (serial0Buffered) {
268 while (transmit0IsBusy)
280 char Serial0GetChar (void)
283 if (serial0Buffered) {
284 while (receive0BufferHead==receive0BufferTail)
286 c=receive0Buffer[receive0BufferTail];
287 ES0=0; // disable serial interrupts
288 receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
289 ES0=1; // enable serial interrupts
299 void Serial0SendBreak() {
301 ClockMilliSecondsDelay(2);
305 void Serial0Flush() {
306 ES0=0; // disable interrupts
307 receive0BufferHead=receive0BufferTail=0;
309 if (serial0Buffered) {
311 ES0=1; // enable interrupts
317 /* now let's go for the serial1 stuff, basically it's a replicate of
318 serial0 except it uses timer 1
321 // just to make the code more readable
322 #define S1RBS SERIAL_1_RECEIVE_BUFFER_SIZE
324 // this is a ring buffer and can overflow at anytime!
325 static volatile unsigned char receive1Buffer[S1RBS];
326 static volatile int receive1BufferHead=0;
327 static volatile int receive1BufferTail=0;
328 // no buffering for transmit
329 static volatile char transmit1IsBusy=0;
331 static data unsigned char serial1Buffered;
333 /* Initialize serial1.
335 Available baudrates are from 4800 upto 115200 (using 8-bit timer 1)
336 If baud==0, the port is disabled.
338 If buffered!=0, characters received are buffered using an interrupt
341 void Serial1Init (unsigned long baud, unsigned char buffered) {
344 ES1=0; // disable interrupt
345 SCON1 &= 0xef; // disable receiver
346 return; // and don't touch it
349 ES1 = 0; // disable channel 1 interrupt
350 TR1 = 0; // stop timer 1
352 // set 8 bit uart with variable baud from timer 1
353 // enable receiver and clear RI and TI
356 WDCON |= 0x80; // clock is 16x bitrate
357 CKCON|=0x10; // timer uses xtal/4
359 TMOD = (TMOD&0x0f) | 0x20; // timer 1 is an 8bit auto-reload counter
364 serial1Buffered=buffered;
367 RI_1=TI_1=0; // clear "pending" interrupts
368 ES1 = 1; // enable serial channel 1 interrupt
370 RI_1=0; // receive buffer empty
371 TI_1=1; // transmit buffer empty
375 void Serial1Baud(unsigned long baud) {
377 baud=-((long)OSCILLATOR/(32*baud));
379 TF1=0; // clear overflow flag
380 TR1=1; // start timer
383 void Serial1IrqHandler (void) interrupt 7 {
385 receive1Buffer[receive1BufferHead]=SBUF1;
386 receive1BufferHead=(receive1BufferHead+1)&(S1RBS-1);
387 if (receive1BufferHead==receive1BufferTail) /* buffer overrun, sorry :) */
388 receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
397 char Serial1CharArrived(void) {
398 if (serial1Buffered) {
399 if (receive1BufferHead!=receive1BufferTail)
400 return receive1Buffer[receive1BufferTail];
408 void Serial1PutChar (char c)
410 if (serial1Buffered) {
411 while (transmit1IsBusy)
423 char Serial1GetChar (void)
426 if (serial1Buffered) {
427 while (receive1BufferHead==receive1BufferTail)
429 c=receive1Buffer[receive1BufferTail];
430 ES1=0; // disable serial interrupts
431 receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
432 ES1=1; // enable serial interrupts
442 void Serial1SendBreak() {
444 ClockMilliSecondsDelay(2);
448 void Serial1Flush() {
449 ES1=0; // disable interrupts
450 receive1BufferHead=receive1BufferTail=0;
452 if (serial1Buffered) {
454 ES1=1; // enable interrupts
460 // now let's go for the clock stuff
462 // these REALLY need to be in data space for the irq routine!
463 static data unsigned long milliSeconds=0;
464 static data unsigned int timer0ReloadValue;
467 unsigned long timerReloadValue=OSCILLATOR/1000;
470 case 4: timerReloadValue/=4; break;
471 case 1: // not tested yet
472 case 2: // not tested yet
473 default: timerReloadValue/=2; break;
475 timer0ReloadValue=~timerReloadValue;
476 // initialise timer 0
477 ET0=0; // disable timer interrupts initially
478 TCON = (TCON&0xcc)|0x00; // stop timer, clear overflow
479 TMOD = (TMOD&0xf0)|0x01; // 16 bit counter
480 CKCON|=0x08; // timer uses xtal/4
482 TL0=timer0ReloadValue&0xff;
483 TH0=timer0ReloadValue>>8;
485 ET0=1; // enable timer interrupts
486 TR0=1; // start timer
489 // This needs to be SUPER fast. What we really want is:
492 void junk_ClockIrqHandler (void) interrupt 10 {
493 TL0=timer0ReloadValue&0xff;
494 TH0=timer0ReloadValue>>8;
498 // but look at the code, and the pushes and pops, so:
499 void ClockIrqHandler (void) interrupt 1 _naked
504 mov _TL0,_timer0ReloadValue
505 mov _TH0,_timer0ReloadValue+1
508 cjne a,_milliSeconds+0,_ClockIrqHandlerDone
510 cjne a,_milliSeconds+1,_ClockIrqHandlerDone
512 cjne a,_milliSeconds+2,_ClockIrqHandlerDone
514 _ClockIrqHandlerDone:
522 // we can't just use milliSeconds
523 unsigned long ClockTicks(void) {
531 void ClockMilliSecondsDelay(unsigned long delay) {
532 long ms=ClockTicks()+delay;
534 while (ms>ClockTicks())
538 // stolen from Kevin Vigor, works only for TINI at default speed
539 void ClockMicroSecondsDelay(unsigned int delay) {
540 delay; /* shut compiler up. */
544 ; delay is in dpl/dph
549 orl a, r1 ; quick out for zero case.
553 cjne r0, #0, _usDelayLoop
564 djnz r0, _usDelayLoop ; 3 cycles x 1 = 3 cycles
566 ; we want 9.216, but more is better
568 djnz r1, _usDelayLoop