eefc203836b8484d2a7d2f7914518c53e45b4436
[fw/sdcc] / device / lib / ds400 / tinibios.c
1 /*-------------------------------------------------------------------------
2   tinibios.c - startup and serial routines for the DS80C400 (tested on TINIM400)
3   
4    Written By - Johan Knol, johan.knol@iduna.nl
5    
6    Further hacked by Kevin Vigor with invaluable assistance from Bob Heise.
7     
8    This program is free software; you can redistribute it and/or modify it
9    under the terms of the GNU General Public License as published by the
10    Free Software Foundation; either version 2, or (at your option) any
11    later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21    
22    In other words, you are welcome to use, share and improve this program.
23    You are forbidden to forbid anyone else to use, share and improve
24    what you give them.   Help stamp out software-hoarding!  
25 -------------------------------------------------------------------------*/
26
27 #include <tinibios.h>
28 #include <ds400rom.h>
29
30 #include <stdio.h>
31
32 #define TIMED_ACCESS(sfr,value) { TA=0xaa; TA=0x55; sfr=value; }
33
34 #undef OSCILLATOR
35 #define OSCILLATOR 14745600
36
37 unsigned char _sdcc_external_startup(void)
38 {
39     IE = 0; // Disable all interrupts.
40
41     PSW = 0;
42     
43   _asm
44     ; save the 24-bit return address
45     pop ar2; msb
46     pop ar1
47     pop ar0; lsb
48
49
50     mov _ESP,#0x00; reinitialize the stack
51     mov _SP,#0x00
52
53     ; restore the 24-bit return address
54     push ar0; lsb
55     push ar1
56     push ar2; msb
57   _endasm;    
58     
59     // Stub: call rom_init here, then fixup IVT.
60     // 
61     
62     Serial0Init(1, 0); // baud argument ignored.
63     
64     IE = 0x80; // Enable interrupts.
65     
66     return 0;
67 }
68
69 // now the serial0 stuff
70
71 // just to make the code more readable 
72 #define S0RBS SERIAL_0_RECEIVE_BUFFER_SIZE
73
74 // this is a ring buffer and can overflow at anytime!
75 static volatile unsigned char receive0Buffer[S0RBS];
76 static volatile int receive0BufferHead=0;
77 static volatile int receive0BufferTail=0;
78 // no buffering for transmit
79 static volatile char transmit0IsBusy=0;
80
81 static data unsigned char serial0Buffered;
82
83 /* Initialize serial0.
84
85    Available baudrates are from 110 upto 115200 (using 16-bit timer 2)
86    If baud==0, the port is disabled.
87
88    If buffered!=0, characters received are buffered using an interrupt
89 */
90
91
92 #define SERIAL0_BAUDRATE 115200
93 #define TIMER_RELOAD (65536 - ((OSCILLATOR) / (32 * SERIAL0_BAUDRATE)))
94
95 void Serial0Init (unsigned long baud, unsigned char buffered) {
96   
97   ES0 = 0; // disable serial channel 0 interrupt
98
99 #if 0    
100   // Need no port setup, done by boot rom.
101   baud;
102 #else
103     SCON0 = 0x5A; // 10 bit serial 0, use timer baud rate, enable recieving
104     RCAP2H = (TIMER_RELOAD >> 8) & 0xff;
105     RCAP2L = TIMER_RELOAD & 0xff;
106     T2CON = 0x30; // Enable timer 2 for serial port
107     TR2 = 1; // Set timer 2 to run
108     
109     baud;
110 #endif    
111
112   serial0Buffered=buffered;
113  
114  if (buffered) {
115     installInterrupt(Serial0IrqHandler, 0x23);
116     RI_0=TI_0=0; // clear "pending" interrupts
117     ES0 = 1; // enable serial channel 0 interrupt
118   } else {
119     RI_0=0; // receive buffer empty
120     TI_0=1; // transmit buffer empty
121   }
122 }
123
124 void Serial0SwitchToBuffered(void)
125 {
126     IE &= ~0x80;
127     
128     serial0Buffered = 1;
129     installInterrupt(Serial0IrqHandler, 0x23);
130     RI_0=TI_0=0; // clear "pending" interrupts
131     ES0 = 1; // enable serial channel 0 interrupt
132     
133     IE |= 0x80;
134 }
135
136 void Serial0IrqHandler (void) interrupt 4 {
137   if (RI_0) {
138     receive0Buffer[receive0BufferHead]=SBUF0;
139     receive0BufferHead=(receive0BufferHead+1)&(S0RBS-1);
140     if (receive0BufferHead==receive0BufferTail) {
141       /* buffer overrun, sorry :) */
142       receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
143     }
144     RI_0=0;
145   }
146   if (TI_0) {
147     TI_0=0;
148     transmit0IsBusy=0;
149   }
150 }
151
152 char Serial0CharArrived(void) {
153   if (serial0Buffered) {
154     if (receive0BufferHead!=receive0BufferTail)
155       return receive0Buffer[receive0BufferTail];
156   } else {
157     if (RI_0)
158       return SBUF0;
159   }
160   return 0;
161 }
162
163 void Serial0PutChar (char c)
164 {
165   if (serial0Buffered) {
166     while (transmit0IsBusy)
167       ;
168     transmit0IsBusy=1;
169     SBUF0=c;
170   } else {
171     while (!TI_0)
172       ;
173     TI_0 = 0;
174     SBUF0=c;
175   }
176 }
177
178 char Serial0GetChar (void)
179 {
180   char c;
181   if (serial0Buffered) {
182     while (receive0BufferHead==receive0BufferTail)
183       ;
184     c=receive0Buffer[receive0BufferTail];
185     ES0=0; // disable serial interrupts
186     receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1);
187     ES0=1; // enable serial interrupts
188   } else {
189     while (!RI_0)
190       ;
191     c=SBUF0;
192     RI_0=0;
193   }
194   return c;
195 }
196
197 void Serial0SendBreak() {
198   P3 &= ~0x02;
199   ClockMilliSecondsDelay(2);
200   P3 |= 0x02;
201 }
202
203 void Serial0Flush() {
204   ES0=0; // disable interrupts
205   receive0BufferHead=receive0BufferTail=0;
206   RI_0=0;
207   if (serial0Buffered) {
208     TI_0=0;
209     ES0=1; // enable interrupts
210   } else {
211     TI_0=1;
212   }
213 }
214
215 // now let's go for the clock stuff - on the DS400, we can just
216 // use the ROM's millisecond timer, running off timer 0.
217 // 
218 // for now, this timer runs too fast by about 20%. We need an implementation of
219 // task_settickreload to fix this.
220
221 void ClockInit() {
222     // nada, all done by DSS_rom_init
223 }
224
225 // we can't just use milliSeconds
226 unsigned long ClockTicks(void) {
227     return DSS_gettimemillis();
228 }
229
230 void ClockMilliSecondsDelay(unsigned long delay) {
231   unsigned long ms = DSS_gettimemillis() + delay;
232
233     while (ms > DSS_gettimemillis())
234         ;
235 }