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);
49 // use internal 4k RAM as data(stack) memory at 0x400000 and
50 // move CANx memory access to 0x401000 and upwards
51 // use !CE* for program and/or data memory access
52 TIMED_ACCESS(MCON,0xaf);
54 // select default cpu speed
58 ; save the 24-bit return address
64 mov _ESP,#0x00; reinitialize the stack
67 mov _TA,#0xaa; timed access
69 mov _ACON,#0x06; 24 bit addresses, 10 bit stack at 0x400000
71 ; restore the 24-bit return address
77 // Copy the Interrupt Vector Table (128 bytes) from 0x10000 to 0x100000
78 // This isn't needed for older bootloaders than the 0515, but it won't harm
86 mov dps,#0x00 ; make sure no autoincrement in progress
87 mov dptr,#0x10000 ; from
88 inc dps ; switch to alternate dptr
89 mov dptr,#0x100000 ; to
99 djnz b,_Startup390CopyIVT
109 // global interrupt enable, all masks cleared
110 // let the Gods be with us :)
113 Serial0Init(SERIAL_0_BAUD,1);
114 //Serial1Init(SERIAL_1_BAUD,1);
119 // signal _sdcc_gsinit_startup to initialize data (call _sdcc_init_data)
123 /* Set the cpu speed in clocks per machine cycle, valid values are:
124 1024: Power management mode
126 2: Use frequency multiplier (2x)
127 1: Use frequency multiplier (4x) (Don't do this on a TINI at 18.432MHz)
129 TODO: TINI seems to support only 2 and 4: write only bits in PMR ?
132 unsigned int cpuSpeed;
134 void CpuSpeed(unsigned int speed) {
136 while (0 && (EXIF&0x04))
137 ; // cpu operates from ring buffer
138 PMR = 0x80; // div4, CTM off, multiplier 2x
142 PMR=0x88; // div4, CTM off, multiplier 4x
143 PMR=0x98; // div4, CTM on, multiplier 4x
144 while ((EXIF&0x08)==0) {
145 ; // wait for the multiplier to be ready
147 PMR = 0x18; // use multiplier
151 PMR=0x90; // div4, CTM on, multilier 2x
152 while ((EXIF&0x08)==0) {
153 ; // wait for the multiplier to be ready
155 PMR = 0x10; // use multiplier
163 PMR = 0xc0; // div1024, CTM off
169 // now the serial0 stuff
171 // just to make the code more readable
172 #define S0RBS SERIAL_0_RECEIVE_BUFFER_SIZE
174 // this is a ring buffer and can overflow at anytime!
175 static volatile unsigned char receive0Buffer[S0RBS];
176 static volatile int receive0BufferHead=0;
177 static volatile int receive0BufferTail=0;
178 // no buffering for transmit
179 static volatile char transmit0IsBusy=0;
181 static data unsigned char serial0Buffered;
183 /* Initialize serial0.
185 Available baudrates are from 110 upto 115200 (using 16-bit timer 2)
186 If baud==0, the port is disabled.
188 If buffered!=0, characters received are buffered using an interrupt
191 void Serial0Init (unsigned long baud, unsigned char buffered) {
194 ES0=0; // disable interrupts
195 SCON0 &= 0xef; // disable receiver
199 ES0 = 0; // disable serial channel 0 interrupt
200 TR2 = 0; // stop timer 2
202 // set 8 bit uart with variable baud from timer 1/2
203 // enable receiver and clear RI and TI
206 PCON |= 0x80; // clock is 16x bitrate
207 CKCON|=0x20; // timer uses xtal/4
209 T2MOD=0; // no fancy functions
210 T2CON=0x34; // start timer as a baudrate generator for serial0
215 serial0Buffered=buffered;
218 RI_0=TI_0=0; // clear "pending" interrupts
219 ES0 = 1; // enable serial channel 0 interrupt
221 RI_0=0; // receive buffer empty
222 TI_0=1; // transmit buffer empty
226 void Serial0Baud(unsigned long baud) {
228 baud=-((long)OSCILLATOR/(32*baud));
231 TF2=0; // clear overflow flag
232 TR2=1; // start timer
235 void Serial0IrqHandler (void) interrupt 4 {
237 receive0Buffer[receive0BufferHead]=SBUF0;
238 receive0BufferHead=(receive0BufferHead+1)&(S0RBS-1);
239 if (receive0BufferHead==receive0BufferTail) {
240 /* buffer overrun, sorry :) */
241 receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
251 char Serial0CharArrived(void) {
252 if (serial0Buffered) {
253 if (receive0BufferHead!=receive0BufferTail)
254 return receive0Buffer[receive0BufferTail];
262 void Serial0PutChar (char c)
264 if (serial0Buffered) {
265 while (transmit0IsBusy)
277 char Serial0GetChar (void)
280 if (serial0Buffered) {
281 while (receive0BufferHead==receive0BufferTail)
283 c=receive0Buffer[receive0BufferTail];
284 ES0=0; // disable serial interrupts
285 receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
286 ES0=1; // enable serial interrupts
296 void Serial0SendBreak() {
298 ClockMilliSecondsDelay(2);
302 void Serial0Flush() {
303 ES0=0; // disable interrupts
304 receive0BufferHead=receive0BufferTail=0;
306 if (serial0Buffered) {
308 ES0=1; // enable interrupts
314 /* now let's go for the serial1 stuff, basically it's a replicate of
315 serial0 except it uses timer 1
318 // just to make the code more readable
319 #define S1RBS SERIAL_1_RECEIVE_BUFFER_SIZE
321 // this is a ring buffer and can overflow at anytime!
322 static volatile unsigned char receive1Buffer[S1RBS];
323 static volatile int receive1BufferHead=0;
324 static volatile int receive1BufferTail=0;
325 // no buffering for transmit
326 static volatile char transmit1IsBusy=0;
328 static data unsigned char serial1Buffered;
330 /* Initialize serial1.
332 Available baudrates are from 4800 upto 115200 (using 8-bit timer 1)
333 If baud==0, the port is disabled.
335 If buffered!=0, characters received are buffered using an interrupt
338 void Serial1Init (unsigned long baud, unsigned char buffered) {
341 ES1=0; // disable interrupt
342 SCON1 &= 0xef; // disable receiver
345 ES1 = 0; // disable channel 1 interrupt
346 TR1 = 0; // stop timer 1
348 // set 8 bit uart with variable baud from timer 1
349 // enable receiver and clear RI and TI
352 WDCON |= 0x80; // clock is 16x bitrate
353 CKCON|=0x10; // timer uses xtal/4
355 TMOD = (TMOD&0x0f) | 0x20; // timer 1 is an 8bit auto-reload counter
360 serial0Buffered=buffered;
363 RI_1=TI_1=0; // clear "pending" interrupts
364 ES1 = 1; // enable serial channel 1 interrupt
366 RI_1=0; // receive buffer empty
367 TI_1=1; // transmit buffer empty
371 void Serial1Baud(unsigned long baud) {
373 baud=-((long)OSCILLATOR/(32*baud));
375 TF1=0; // clear overflow flag
376 TR1=1; // start timer
379 void Serial1IrqHandler (void) interrupt 7 {
381 receive1Buffer[receive1BufferHead]=SBUF1;
382 receive1BufferHead=(receive1BufferHead+1)&(S1RBS-1);
383 if (receive1BufferHead==receive1BufferTail) /* buffer overrun, sorry :) */
384 receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
393 char Serial1CharArrived(void) {
394 if (serial1Buffered) {
395 if (receive1BufferHead!=receive1BufferTail)
396 return receive1Buffer[receive1BufferTail];
404 void Serial1PutChar (char c)
406 if (serial1Buffered) {
407 while (transmit1IsBusy)
419 char Serial1GetChar (void)
422 if (serial1Buffered) {
423 while (receive1BufferHead==receive1BufferTail)
425 c=receive1Buffer[receive1BufferTail];
426 ES1=0; // disable serial interrupts
427 receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1);
428 ES1=1; // enable serial interrupts
438 void Serial1SendBreak() {
440 ClockMilliSecondsDelay(2);
444 void Serial1Flush() {
445 ES1=0; // disable interrupts
446 receive1BufferHead=receive1BufferTail=0;
448 if (serial1Buffered) {
450 ES1=1; // enable interrupts
456 // now let's go for the clock stuff
458 //#define TIMER_0_RELOAD_VALUE 18432000L/2/1000 // appr. 1ms
460 static data unsigned long milliSeconds=0;
461 static data unsigned int timer0ReloadValue;
464 unsigned long timerReloadValue=OSCILLATOR/1000;
467 case 4: timerReloadValue/=4; break;
468 case 1: // not tested yet
469 case 2: // not tested yet
470 default: timerReloadValue/=2; break;
472 timer0ReloadValue=timerReloadValue;
473 // initialise timer 0
474 ET0=0; // disable timer interrupts initially
475 TCON = (TCON&0xcc)|0x00; // stop timer, clear overflow
476 TMOD = (TMOD&0xf0)|0x01; // 16 bit counter
477 CKCON|=0x80; // timer uses xtal/4
479 TL0=~(timer0ReloadValue&0xff);
480 TH0=~(timer0ReloadValue>>8);
482 ET0=1; // enable timer interrupts
483 TR0=1; // start timer
486 void ClockIrqHandler (void) interrupt 1 {
487 // we have lost some time here
488 TL0=~(timer0ReloadValue&0xff);
489 TH0=~(timer0ReloadValue>>8);
491 // that's all for now :)
494 // we can't just use milliSeconds
495 unsigned long ClockTicks(void) {
503 void ClockMilliSecondsDelay(unsigned long delay) {
504 long ms=ClockTicks()+delay;
506 while (ms>ClockTicks())
510 // stolen from Kevin Vigor, works only for TINI at default speed
511 void ClockMicroSecondsDelay(unsigned int delay) {
512 delay; /* shut compiler up. */
516 ; delay is in dpl/dph
521 orl a, r1 ; quick out for zero case.
525 cjne r0, #0, _usDelayLoop
536 djnz r0, _usDelayLoop ; 3 cycles x 1 = 3 cycles
538 ; we want 9.216, but more is better
540 djnz r1, _usDelayLoop