1 /*
2 * PROJECT: ReactOS ComPort Library for NEC PC-98 series
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Provides a serial port library for KDCOM, INIT, and FREELDR
5 * COPYRIGHT: Copyright 2020 Dmitry Borisov (di.sean@protonmail.com)
6 */
7
8 /* Note: ns16550 code from cportlib.c */
9
10 /* INCLUDES *******************************************************************/
11
12 #include <intrin.h>
13 #include <ioaccess.h>
14 #include <ntstatus.h>
15 #include <cportlib/cportlib.h>
16 #include <drivers/pc98/serial.h>
17 #include <drivers/pc98/sysport.h>
18 #include <drivers/pc98/pit.h>
19 #include <drivers/pc98/cpu.h>
20
21 /* GLOBALS ********************************************************************/
22
23 #define TIMEOUT_COUNT 1024 * 200
24
25 static struct
26 {
27 PUCHAR Address;
28 BOOLEAN HasFifo;
29 BOOLEAN FifoEnabled;
30 UCHAR RingIndicator;
31 } Rs232ComPort[] =
32 {
33 { (PUCHAR)0x030, FALSE, FALSE, 0 },
34 { (PUCHAR)0x238, FALSE, FALSE, 0 }
35 };
36
37 static BOOLEAN IsNekoProject = FALSE;
38
39 /* FUNCTIONS ******************************************************************/
40
41 static BOOLEAN
CpIsNekoProject(VOID)42 CpIsNekoProject(VOID)
43 {
44 UCHAR Input[4] = "NP2";
45 UCHAR Output[4] = {0};
46 UCHAR i;
47
48 for (i = 0; i < 3; i++)
49 WRITE_PORT_UCHAR((PUCHAR)0x7EF, Input[i]);
50
51 for (i = 0; i < 3; i++)
52 Output[i] = READ_PORT_UCHAR((PUCHAR)0x7EF);
53
54 return (*(PULONG)Input == *(PULONG)Output);
55 }
56
57 static VOID
CpWait(VOID)58 CpWait(VOID)
59 {
60 UCHAR i;
61
62 for (i = 0; i < 6; i++)
63 WRITE_PORT_UCHAR((PUCHAR)CPU_IO_o_ARTIC_DELAY, 0);
64 }
65
66 VOID
67 NTAPI
CpEnableFifo(IN PUCHAR Address,IN BOOLEAN Enable)68 CpEnableFifo(
69 IN PUCHAR Address,
70 IN BOOLEAN Enable)
71 {
72 /* Set FIFO and clear the receive/transmit buffers */
73 if (Address == Rs232ComPort[0].Address && Rs232ComPort[0].HasFifo)
74 {
75 if (Enable)
76 WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_FIFO_CONTROL,
77 SER_FCR_ENABLE | SER_FCR_RCVR_RESET | SER_FCR_TXMT_RESET);
78 else
79 WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_FIFO_CONTROL, SER_FCR_DISABLE);
80 Rs232ComPort[0].FifoEnabled = Enable;
81 }
82 else if (Address == Rs232ComPort[1].Address && Rs232ComPort[1].HasFifo)
83 {
84 if (Enable)
85 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_FIFO_CONTROL,
86 SER_FCR_ENABLE | SER_FCR_RCVR_RESET | SER_FCR_TXMT_RESET);
87 else
88 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_FIFO_CONTROL, SER_FCR_DISABLE);
89 Rs232ComPort[1].FifoEnabled = Enable;
90 }
91 CpWait();
92 }
93
94 VOID
95 NTAPI
CpSetBaud(IN PCPPORT Port,IN ULONG BaudRate)96 CpSetBaud(
97 IN PCPPORT Port,
98 IN ULONG BaudRate)
99 {
100 UCHAR Lcr;
101 USHORT Count;
102 TIMER_CONTROL_PORT_REGISTER TimerControl;
103
104 if (Port->Address == Rs232ComPort[0].Address)
105 {
106 if (Rs232ComPort[0].HasFifo)
107 WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_DIVISOR_LATCH, SER1_DLR_MODE_LEGACY);
108
109 TimerControl.BcdMode = FALSE;
110 TimerControl.OperatingMode = PitOperatingMode3;
111 TimerControl.AccessMode = PitAccessModeLowHigh;
112 TimerControl.Channel = PitChannel2;
113 if (IsNekoProject)
114 {
115 /* The horrible text input lag happens by about 6 seconds on my PC */
116 Count = 3;
117 }
118 else
119 {
120 Count = (READ_PORT_UCHAR((PUCHAR)0x42) & 0x20) ?
121 (TIMER_FREQUENCY_1 / (BaudRate * 16)) : (TIMER_FREQUENCY_2 / (BaudRate * 16));
122 }
123 Write8253Timer(TimerControl, Count);
124
125 /* Save baud rate in port */
126 Port->BaudRate = BaudRate;
127 }
128 else if (Port->Address == Rs232ComPort[1].Address)
129 {
130 /* Set the DLAB on */
131 Lcr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_LINE_CONTROL);
132 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_LINE_CONTROL, Lcr | SER2_LCR_DLAB);
133
134 /* Set the baud rate */
135 Count = SER2_CLOCK_RATE / BaudRate;
136 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_DIVISOR_LATCH_LSB, Count & 0xFF);
137 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_DIVISOR_LATCH_MSB, (Count >> 8) & 0xFF);
138
139 /* Reset DLAB */
140 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_LINE_CONTROL, Lcr & ~SER2_LCR_DLAB);
141
142 /* Save baud rate in port */
143 Port->BaudRate = BaudRate;
144 }
145 }
146
147 NTSTATUS
148 NTAPI
CpInitialize(IN PCPPORT Port,IN PUCHAR Address,IN ULONG BaudRate)149 CpInitialize(
150 IN PCPPORT Port,
151 IN PUCHAR Address,
152 IN ULONG BaudRate)
153 {
154 SYSTEM_CONTROL_PORT_C_REGISTER SystemControl;
155 UCHAR FifoStatus;
156
157 if (Port == NULL || Address == NULL || BaudRate == 0)
158 return STATUS_INVALID_PARAMETER;
159
160 if (!CpDoesPortExist(Address))
161 return STATUS_NOT_FOUND;
162
163 /* Initialize port data */
164 Port->Address = Address;
165 Port->BaudRate = 0;
166 Port->Flags = 0;
167
168 IsNekoProject = CpIsNekoProject();
169
170 if (Port->Address == Rs232ComPort[0].Address)
171 {
172 /* FIFO test */
173 FifoStatus = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_INTERRUPT_ID) & SER1_IIR_FIFOS_ENABLED;
174 CpWait();
175 Rs232ComPort[0].HasFifo = ((READ_PORT_UCHAR((PUCHAR)SER1_IO_i_INTERRUPT_ID) & SER1_IIR_FIFOS_ENABLED) != FifoStatus);
176
177 /* Disable the interrupts */
178 SystemControl.Bits = READ_PORT_UCHAR((PUCHAR)PPI_IO_i_PORT_C);
179 SystemControl.InterruptEnableRxReady = FALSE;
180 SystemControl.InterruptEnableTxEmpty = FALSE;
181 SystemControl.InterruptEnableTxReady = FALSE;
182 WRITE_PORT_UCHAR((PUCHAR)PPI_IO_o_PORT_C, SystemControl.Bits);
183
184 /* Turn off FIFO */
185 if (Rs232ComPort[0].HasFifo)
186 CpEnableFifo(Address, FALSE);
187
188 /* Set the baud rate */
189 CpSetBaud(Port, BaudRate);
190
191 /* Software reset */
192 WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, 0);
193 CpWait();
194 WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, 0);
195 CpWait();
196 WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, 0);
197 CpWait();
198 WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, SER1_COMMMAND_IR);
199 CpWait();
200
201 /* Mode instruction - asynchronous mode, 8 data bits, 1 stop bit, no parity, 16x clock divisor */
202 WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND,
203 SER1_MODE_LENGTH_8 | SER1_MODE_1_STOP | SER1_MODE_CLOCKx16);
204 CpWait();
205
206 /* Command instruction - transmit enable, turn on DTR and RTS, receive enable, clear error flag */
207 WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND,
208 SER1_COMMMAND_TxEN | SER1_COMMMAND_DTR |
209 SER1_COMMMAND_RxEN | SER1_COMMMAND_ER | SER1_COMMMAND_RTS);
210 CpWait();
211
212 /* Disable the interrupts again */
213 WRITE_PORT_UCHAR((PUCHAR)PPI_IO_o_PORT_C, SystemControl.Bits);
214
215 /* Turn on FIFO */
216 if (Rs232ComPort[0].HasFifo)
217 CpEnableFifo(Address, TRUE);
218
219 /* Read junk out of the data register */
220 if (Rs232ComPort[0].HasFifo)
221 (VOID)READ_PORT_UCHAR((PUCHAR)SER1_IO_i_RECEIVER_BUFFER);
222 else
223 (VOID)READ_PORT_UCHAR((PUCHAR)SER1_IO_i_DATA);
224
225 return STATUS_SUCCESS;
226 }
227 else if (Port->Address == Rs232ComPort[1].Address)
228 {
229 /* Disable the interrupts */
230 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_LINE_CONTROL, 0);
231 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_INTERRUPT_EN, 0);
232
233 /* Turn on DTR, RTS and OUT2 */
234 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL,
235 SER2_MCR_DTR_STATE | SER2_MCR_RTS_STATE | SER2_MCR_OUT_2);
236
237 /* Set the baud rate */
238 CpSetBaud(Port, BaudRate);
239
240 /* Set 8 data bits, 1 stop bit, no parity, no break */
241 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_LINE_CONTROL,
242 SER2_LCR_LENGTH_8 | SER2_LCR_ST1 | SER2_LCR_NO_PARITY);
243
244 /* FIFO test */
245 Rs232ComPort[1].HasFifo = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_INTERRUPT_ID) & SER2_IIR_HAS_FIFO;
246
247 /* Turn on FIFO */
248 if (Rs232ComPort[1].HasFifo)
249 CpEnableFifo(Address, TRUE);
250
251 /* Read junk out of the RBR */
252 (VOID)READ_PORT_UCHAR((PUCHAR)SER2_IO_i_RECEIVER_BUFFER);
253
254 return STATUS_SUCCESS;
255 }
256
257 return STATUS_NOT_FOUND;
258 }
259
260 static BOOLEAN
ComPortTest1(IN PUCHAR Address)261 ComPortTest1(IN PUCHAR Address)
262 {
263 /*
264 * See "Building Hardware and Firmware to Complement Microsoft Windows Headless Operation"
265 * Out-of-Band Management Port Device Requirements:
266 * The device must act as a 16550 or 16450 UART.
267 * Windows Server 2003 will test this device using the following process:
268 * 1. Save off the current modem status register.
269 * 2. Place the UART into diagnostic mode (The UART is placed into loopback mode
270 * by writing SERIAL_MCR_LOOP to the modem control register).
271 * 3. The modem status register is read and the high bits are checked. This means
272 * SERIAL_MSR_CTS, SERIAL_MSR_DSR, SERIAL_MSR_RI and SERIAL_MSR_DCD should
273 * all be clear.
274 * 4. Place the UART in diagnostic mode and turn on OUTPUT (Loopback Mode and
275 * OUTPUT are both turned on by writing (SERIAL_MCR_LOOP | SERIAL_MCR_OUT1)
276 * to the modem control register).
277 * 5. The modem status register is read and the ring indicator is checked.
278 * This means SERIAL_MSR_RI should be set.
279 * 6. Restore original modem status register.
280 *
281 * REMARK: Strangely enough, the Virtual PC 2007 virtual machine
282 * doesn't pass this test.
283 */
284
285 BOOLEAN RetVal = FALSE;
286 UCHAR Mcr, Msr;
287
288 /* Save the Modem Control Register */
289 Mcr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_MODEM_CONTROL);
290
291 /* Enable loop (diagnostic) mode (set Bit 4 of the MCR) */
292 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL, SER2_MCR_LOOPBACK);
293
294 /* Clear all modem output bits */
295 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL, SER2_MCR_LOOPBACK);
296
297 /* Read the Modem Status Register */
298 Msr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_MODEM_STATUS);
299
300 /*
301 * The upper nibble of the MSR (modem output bits) must be
302 * equal to the lower nibble of the MCR (modem input bits).
303 */
304 if ((Msr & (SER_MSR_CTS | SER_MSR_DSR | SER_MSR_RI | SER_MSR_DCD)) == 0x00)
305 {
306 /* Set all modem output bits */
307 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL,
308 SER2_MCR_OUT_1 | SER2_MCR_LOOPBACK); // Windows
309 /* ReactOS
310 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL,
311 SER2_MCR_DTR_STATE | SER2_MCR_RTS_STATE |
312 SER2_MCR_OUT_1 | SER2_MCR_OUT_2 | SER2_MCR_LOOPBACK);
313 */
314
315 /* Read the Modem Status Register */
316 Msr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_MODEM_STATUS);
317
318 /*
319 * The upper nibble of the MSR (modem output bits) must be
320 * equal to the lower nibble of the MCR (modem input bits).
321 */
322 if (Msr & SER_MSR_RI) // Windows
323 // if (Msr & (SER_MSR_CTS | SER_MSR_DSR | SER_MSR_RI | SER_MSR_DCD) == 0xF0) // ReactOS
324 {
325 RetVal = TRUE;
326 }
327 }
328
329 /* Restore the MCR */
330 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL, Mcr);
331
332 return RetVal;
333 }
334
335 static BOOLEAN
ComPortTest2(IN PUCHAR Address)336 ComPortTest2(IN PUCHAR Address)
337 {
338 /*
339 * This test checks whether the 16450/16550 scratch register is available.
340 * If not, the serial port is considered as unexisting.
341 */
342
343 UCHAR Byte = 0;
344
345 do
346 {
347 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_SCRATCH, Byte);
348
349 if (READ_PORT_UCHAR((PUCHAR)SER2_IO_i_SCRATCH) != Byte)
350 return FALSE;
351
352 }
353 while (++Byte != 0);
354
355 return TRUE;
356 }
357
358 BOOLEAN
359 NTAPI
CpDoesPortExist(IN PUCHAR Address)360 CpDoesPortExist(IN PUCHAR Address)
361 {
362 UCHAR Data, Status;
363
364 if (Address == Rs232ComPort[0].Address || Address == (PUCHAR)0x41)
365 {
366 Data = READ_PORT_UCHAR(Address);
367 Status = READ_PORT_UCHAR(Address + 2);
368 if ((Data & Status) == 0xFF || (Data | Status) == 0x00)
369 return FALSE;
370 else
371 return TRUE;
372 }
373 else if (Address == Rs232ComPort[1].Address)
374 {
375 return (ComPortTest1(Address) || ComPortTest2(Address));
376 }
377
378 return FALSE;
379 }
380
381 UCHAR
382 NTAPI
CpReadLsr(IN PCPPORT Port,IN UCHAR ExpectedValue)383 CpReadLsr(
384 IN PCPPORT Port,
385 IN UCHAR ExpectedValue)
386 {
387 UCHAR Lsr, Msr;
388 SYSTEM_CONTROL_PORT_B_REGISTER SystemControl;
389
390 if (Port->Address == Rs232ComPort[0].Address)
391 {
392 /* Read the LSR and check if the expected value is present */
393 if (Rs232ComPort[0].HasFifo)
394 {
395 Lsr = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_LINE_STATUS);
396 if (!(Lsr & ExpectedValue))
397 {
398 Msr = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_MODEM_STATUS);
399
400 /* If the ring indicator reaches 3, we've seen this on/off twice */
401 Rs232ComPort[0].RingIndicator |= (Msr & SER_MSR_RI) ? 1 : 2;
402 if (Rs232ComPort[0].RingIndicator == 3)
403 Port->Flags |= CPPORT_FLAG_MODEM_CONTROL;
404 }
405 }
406 else
407 {
408 Lsr = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_STATUS);
409 if (!(Lsr & ExpectedValue))
410 {
411 SystemControl.Bits = READ_PORT_UCHAR((PUCHAR)PPI_IO_i_PORT_B);
412
413 /* If the ring indicator reaches 3, we've seen this on/off twice */
414 Rs232ComPort[0].RingIndicator |= SystemControl.RingIndicator ? 1 : 2;
415 if (Rs232ComPort[0].RingIndicator == 3)
416 Port->Flags |= CPPORT_FLAG_MODEM_CONTROL;
417 }
418 }
419
420 return Lsr;
421 }
422 else if (Port->Address == Rs232ComPort[1].Address)
423 {
424 /* Read the LSR and check if the expected value is present */
425 Lsr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_LINE_STATUS);
426 if (!(Lsr & ExpectedValue))
427 {
428 Msr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_MODEM_STATUS);
429
430 /* If the indicator reaches 3, we've seen this on/off twice */
431 Rs232ComPort[1].RingIndicator |= (Msr & SER_MSR_RI) ? 1 : 2;
432 if (Rs232ComPort[1].RingIndicator == 3)
433 Port->Flags |= CPPORT_FLAG_MODEM_CONTROL;
434 }
435
436 return Lsr;
437 }
438
439 return 0;
440 }
441
442 USHORT
443 NTAPI
CpGetByte(IN PCPPORT Port,OUT PUCHAR Byte,IN BOOLEAN Wait,IN BOOLEAN Poll)444 CpGetByte(
445 IN PCPPORT Port,
446 OUT PUCHAR Byte,
447 IN BOOLEAN Wait,
448 IN BOOLEAN Poll)
449 {
450 UCHAR Lsr;
451 ULONG LimitCount = Wait ? TIMEOUT_COUNT : 1;
452 UCHAR SuccessFlags, ErrorFlags;
453 BOOLEAN FifoEnabled;
454
455 /* Handle early read-before-init */
456 if (!Port->Address)
457 return CP_GET_NODATA;
458
459 if (Port->Address == Rs232ComPort[0].Address)
460 {
461 SuccessFlags = Rs232ComPort[0].HasFifo ? SER1_LSR_RxRDY : SER1_STATUS_RxRDY;
462 ErrorFlags = Rs232ComPort[0].HasFifo ? (SER1_LSR_PE | SER1_LSR_OE) :
463 (SER1_STATUS_FE | SER1_STATUS_PE | SER1_STATUS_OE);
464
465 /* If "wait" mode enabled, spin many times, otherwise attempt just once */
466 while (LimitCount--)
467 {
468 /* Read LSR for data ready */
469 Lsr = CpReadLsr(Port, SuccessFlags);
470 if (Lsr & SuccessFlags)
471 {
472 /* If an error happened, clear the byte and fail */
473 if (Lsr & ErrorFlags)
474 {
475 /* Save the last FIFO state */
476 FifoEnabled = Rs232ComPort[0].FifoEnabled;
477
478 /* Turn off FIFO */
479 if (FifoEnabled)
480 CpEnableFifo(Port->Address, FALSE);
481
482 /* Clear error flag */
483 WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND,
484 SER1_COMMMAND_TxEN | SER1_COMMMAND_DTR |
485 SER1_COMMMAND_RxEN | SER1_COMMMAND_ER | SER1_COMMMAND_RTS);
486
487 /* Turn on FIFO */
488 if (FifoEnabled)
489 CpEnableFifo(Port->Address, TRUE);
490
491 *Byte = 0;
492 return CP_GET_ERROR;
493 }
494
495 /* If only polling was requested by caller, return now */
496 if (Poll)
497 return CP_GET_SUCCESS;
498
499 /* Otherwise read the byte and return it */
500 if (Rs232ComPort[0].HasFifo)
501 *Byte = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_RECEIVER_BUFFER);
502 else
503 *Byte = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_DATA);
504
505 /* TODO: Handle CD if port is in modem control mode */
506
507 /* Byte was read */
508 return CP_GET_SUCCESS;
509 }
510 else if (IsNekoProject && Rs232ComPort[0].HasFifo)
511 {
512 /*
513 * Neko Project 21/W doesn't set RxRDY without reading any data from 0x136.
514 * TODO: Check real hardware behavior.
515 */
516 (VOID)READ_PORT_UCHAR((PUCHAR)SER1_IO_i_INTERRUPT_ID);
517 }
518 }
519
520 /* Reset LSR, no data was found */
521 CpReadLsr(Port, 0);
522 }
523 else if (Port->Address == Rs232ComPort[1].Address)
524 {
525 /* If "wait" mode enabled, spin many times, otherwise attempt just once */
526 while (LimitCount--)
527 {
528 /* Read LSR for data ready */
529 Lsr = CpReadLsr(Port, SER2_LSR_DR);
530 if ((Lsr & SER2_LSR_DR) == SER2_LSR_DR)
531 {
532 /* If an error happened, clear the byte and fail */
533 if (Lsr & (SER2_LSR_FE | SER2_LSR_PE | SER2_LSR_OE))
534 {
535 *Byte = 0;
536 return CP_GET_ERROR;
537 }
538
539 /* If only polling was requested by caller, return now */
540 if (Poll)
541 return CP_GET_SUCCESS;
542
543 /* Otherwise read the byte and return it */
544 *Byte = READ_PORT_UCHAR((UCHAR)SER2_IO_i_RECEIVER_BUFFER);
545
546 /* TODO: Handle CD if port is in modem control mode */
547
548 /* Byte was read */
549 return CP_GET_SUCCESS;
550 }
551 }
552
553 /* Reset LSR, no data was found */
554 CpReadLsr(Port, 0);
555 }
556
557 return CP_GET_NODATA;
558 }
559
560 VOID
561 NTAPI
CpPutByte(IN PCPPORT Port,IN UCHAR Byte)562 CpPutByte(
563 IN PCPPORT Port,
564 IN UCHAR Byte)
565 {
566 if (Port->Address == Rs232ComPort[0].Address)
567 {
568 /* TODO: Check if port is in modem control to handle CD */
569
570 if (Rs232ComPort[0].HasFifo)
571 {
572 while ((CpReadLsr(Port, SER1_LSR_TxRDY) & SER1_LSR_TxRDY) == 0)
573 NOTHING;
574
575 WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_TRANSMITTER_BUFFER, Byte);
576 }
577 else
578 {
579 while ((CpReadLsr(Port, SER1_STATUS_TxRDY) & SER1_STATUS_TxRDY) == 0)
580 NOTHING;
581
582 WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_DATA, Byte);
583 }
584 }
585 else if (Port->Address == Rs232ComPort[1].Address)
586 {
587 /* TODO: Check if port is in modem control to handle CD */
588
589 while ((CpReadLsr(Port, SER2_LSR_THR_EMPTY) & SER2_LSR_THR_EMPTY) == 0)
590 NOTHING;
591
592 WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_TRANSMITTER_BUFFER, Byte);
593 }
594 }
595