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