xref: /reactos/sdk/lib/cportlib/cport_pc98.c (revision f308c6a2)
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
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
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
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
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
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
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
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
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
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
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
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