PCem

view src/serial.c @ 139:eee628bf93de

Serial controller is no longer reset when serial port/IRQ updated - fixes mouse on 430VX in Windows 98.
author TomW
date Thu Jul 31 15:57:24 2014 +0100
parents 6ddadbcbcfe0
children
line source
1 #include "ibm.h"
2 #include "io.h"
3 #include "mouse.h"
4 #include "pic.h"
5 #include "serial.h"
6 #include "timer.h"
8 enum
9 {
10 SERIAL_INT_LSR = 1,
11 SERIAL_INT_RECEIVE = 2,
12 SERIAL_INT_TRANSMIT = 4,
13 SERIAL_INT_MSR = 8
14 };
16 SERIAL serial1, serial2;
18 int mousepos=-1;
19 int mousedelay;
21 void serial_reset()
22 {
23 serial1.iir = serial1.ier = serial1.lcr = 0;
24 serial2.iir = serial2.ier = serial2.lcr = 0;
25 mousedelay = 0;
26 serial1.fifo_read = serial1.fifo_write = 0;
27 serial2.fifo_read = serial2.fifo_write = 0;
28 }
30 void serial_update_ints(SERIAL *serial)
31 {
32 int stat = 0;
34 serial->iir = 1;
36 if ((serial->ier & 4) && (serial->int_status & SERIAL_INT_LSR)) /*Line status interrupt*/
37 {
38 stat = 1;
39 serial->iir = 6;
40 }
41 else if ((serial->ier & 1) && (serial->int_status & SERIAL_INT_RECEIVE)) /*Recieved data available*/
42 {
43 stat = 1;
44 serial->iir = 4;
45 }
46 else if ((serial->ier & 2) && (serial->int_status & SERIAL_INT_TRANSMIT)) /*Transmit data empty*/
47 {
48 stat = 1;
49 serial->iir = 2;
50 }
51 else if ((serial->ier & 8) && (serial->int_status & SERIAL_INT_MSR)) /*Modem status interrupt*/
52 {
53 stat = 1;
54 serial->iir = 0;
55 }
57 if (stat && ((serial->mctrl & 8) || PCJR))
58 picintlevel(1 << serial->irq);
59 else
60 picintc(1 << serial->irq);
61 }
63 void serial_write_fifo(SERIAL *serial, uint8_t dat)
64 {
65 // pclog("serial_write_fifo %02X\n", serial->lsr);
66 serial->fifo[serial->fifo_write] = dat;
67 serial->fifo_write = (serial->fifo_write + 1) & 0xFF;
68 if (!(serial->lsr & 1))
69 {
70 serial->lsr |= 1;
71 serial->int_status |= SERIAL_INT_RECEIVE;
72 serial_update_ints(serial);
73 }
74 }
76 uint8_t serial_read_fifo(SERIAL *serial)
77 {
78 if (serial->fifo_read != serial->fifo_write)
79 {
80 serial->dat = serial->fifo[serial->fifo_read];
81 serial->fifo_read = (serial->fifo_read + 1) & 0xFF;
82 }
83 return serial->dat;
84 }
86 void serial_write(uint16_t addr, uint8_t val, void *p)
87 {
88 SERIAL *serial = (SERIAL *)p;
89 // pclog("Write serial %03X %02X %04X:%04X\n",addr,val,CS,pc);
90 switch (addr&7)
91 {
92 case 0:
93 if (serial->lcr & 0x80 && !AMSTRADIO)
94 {
95 serial->dlab1 = val;
96 return;
97 }
98 serial->thr = val;
99 serial->lsr |= 0x20;
100 serial->int_status |= SERIAL_INT_TRANSMIT;
101 serial_update_ints(serial);
102 if (serial->mctrl & 0x10)
103 {
104 serial_write_fifo(serial, val);
105 }
106 break;
107 case 1:
108 if (serial->lcr & 0x80 && !AMSTRADIO)
109 {
110 serial->dlab2 = val;
111 return;
112 }
113 serial->ier = val;
114 serial_update_ints(serial);
115 break;
116 case 3:
117 serial->lcr = val;
118 break;
119 case 4:
120 if ((val & 2) && !(serial->mctrl & 2))
121 {
122 if (serial->rcr_callback)
123 serial->rcr_callback(serial);
124 // pclog("RCR raised! sending M\n");
125 }
126 serial->mctrl = val;
127 if (val & 0x10)
128 {
129 uint8_t new_msr;
131 new_msr = (val & 0x0c) << 4;
132 new_msr |= (val & 0x02) ? 0x10: 0;
133 new_msr |= (val & 0x01) ? 0x20: 0;
135 if ((serial->msr ^ new_msr) & 0x10)
136 new_msr |= 0x01;
137 if ((serial->msr ^ new_msr) & 0x20)
138 new_msr |= 0x02;
139 if ((serial->msr ^ new_msr) & 0x80)
140 new_msr |= 0x08;
141 if ((serial->msr & 0x40) && !(new_msr & 0x40))
142 new_msr |= 0x04;
144 serial->msr = new_msr;
145 }
146 break;
147 case 5:
148 serial->lsr = val;
149 if (serial->lsr & 0x01)
150 serial->int_status |= SERIAL_INT_RECEIVE;
151 if (serial->lsr & 0x1e)
152 serial->int_status |= SERIAL_INT_LSR;
153 if (serial->lsr & 0x20)
154 serial->int_status |= SERIAL_INT_TRANSMIT;
155 serial_update_ints(serial);
156 break;
157 case 6:
158 serial->msr = val;
159 if (serial->msr & 0x0f)
160 serial->int_status |= SERIAL_INT_MSR;
161 serial_update_ints(serial);
162 break;
163 }
164 }
166 uint8_t serial_read(uint16_t addr, void *p)
167 {
168 SERIAL *serial = (SERIAL *)p;
169 uint8_t temp = 0;
170 // pclog("Read serial %03X %04X(%08X):%04X %i %i ", addr, CS, cs, pc, mousedelay, ins);
171 switch (addr&7)
172 {
173 case 0:
174 if (serial->lcr & 0x80 && !AMSTRADIO)
175 {
176 temp = serial->dlab1;
177 break;
178 }
180 serial->lsr &= ~1;
181 serial->int_status &= ~SERIAL_INT_RECEIVE;
182 serial_update_ints(serial);
183 temp = serial_read_fifo(serial);
184 if (serial->fifo_read != serial->fifo_write)
185 serial->recieve_delay = 1000 * TIMER_USEC;
186 break;
187 case 1:
188 if (serial->lcr & 0x80 && !AMSTRADIO)
189 temp = serial->dlab2;
190 else
191 temp = serial->ier;
192 break;
193 case 2:
194 temp = serial->iir;
195 if ((temp & 0xe) == 2)
196 {
197 serial->int_status &= ~SERIAL_INT_TRANSMIT;
198 serial_update_ints(serial);
199 }
200 break;
201 case 3:
202 temp = serial->lcr;
203 break;
204 case 4:
205 temp = serial->mctrl;
206 break;
207 case 5:
208 if (serial->lsr & 0x20)
209 serial->lsr |= 0x40;
210 serial->lsr |= 0x20;
211 temp = serial->lsr;
212 if (serial->lsr & 0x1f)
213 serial->lsr &= ~0x1e;
214 // serial.lsr |= 0x60;
215 serial->int_status &= ~SERIAL_INT_LSR;
216 serial_update_ints(serial);
217 break;
218 case 6:
219 temp = serial->msr;
220 serial->msr &= ~0x0f;
221 serial->int_status &= ~SERIAL_INT_MSR;
222 serial_update_ints(serial);
223 break;
224 }
225 // pclog("%02X\n",temp);
226 return temp;
227 }
229 void serial_recieve_callback(void *p)
230 {
231 SERIAL *serial = (SERIAL *)p;
233 serial->recieve_delay = 0;
235 if (serial->fifo_read != serial->fifo_write)
236 {
237 serial->lsr |= 1;
238 serial->int_status |= SERIAL_INT_RECEIVE;
239 serial_update_ints(serial);
240 }
241 }
243 /*Tandy might need COM1 at 2f8*/
244 void serial1_init(uint16_t addr, int irq)
245 {
246 memset(&serial1, 0, sizeof(serial1));
247 io_sethandler(addr, 0x0008, serial_read, NULL, NULL, serial_write, NULL, NULL, &serial1);
248 serial1.irq = irq;
249 serial1.rcr_callback = NULL;
250 timer_add(serial_recieve_callback, &serial1.recieve_delay, &serial1.recieve_delay, &serial1);
251 }
252 void serial1_set(uint16_t addr, int irq)
253 {
254 serial1_remove();
255 io_sethandler(addr, 0x0008, serial_read, NULL, NULL, serial_write, NULL, NULL, &serial1);
256 serial1.irq = irq;
257 }
258 void serial1_remove()
259 {
260 io_removehandler(0x2e8, 0x0008, serial_read, NULL, NULL, serial_write, NULL, NULL, &serial1);
261 io_removehandler(0x2f8, 0x0008, serial_read, NULL, NULL, serial_write, NULL, NULL, &serial1);
262 io_removehandler(0x3e8, 0x0008, serial_read, NULL, NULL, serial_write, NULL, NULL, &serial1);
263 io_removehandler(0x3f8, 0x0008, serial_read, NULL, NULL, serial_write, NULL, NULL, &serial1);
264 }
266 void serial2_init(uint16_t addr, int irq)
267 {
268 memset(&serial2, 0, sizeof(serial2));
269 io_sethandler(addr, 0x0008, serial_read, NULL, NULL, serial_write, NULL, NULL, &serial2);
270 serial2.irq = irq;
271 serial2.rcr_callback = NULL;
272 timer_add(serial_recieve_callback, &serial2.recieve_delay, &serial2.recieve_delay, &serial2);
273 }
274 void serial2_set(uint16_t addr, int irq)
275 {
276 serial2_remove();
277 io_sethandler(addr, 0x0008, serial_read, NULL, NULL, serial_write, NULL, NULL, &serial2);
278 serial2.irq = irq;
279 }
280 void serial2_remove()
281 {
282 io_removehandler(0x2e8, 0x0008, serial_read, NULL, NULL, serial_write, NULL, NULL, &serial2);
283 io_removehandler(0x2f8, 0x0008, serial_read, NULL, NULL, serial_write, NULL, NULL, &serial2);
284 io_removehandler(0x3e8, 0x0008, serial_read, NULL, NULL, serial_write, NULL, NULL, &serial2);
285 io_removehandler(0x3f8, 0x0008, serial_read, NULL, NULL, serial_write, NULL, NULL, &serial2);
286 }