== Serial (Optional Add-On) Declared in ``. [IMPORTANT] ==== This header is *not* pulled in by ``. Include it explicitly: [source,c] ---- #include #include ---- A program that never references `jlSerial*` pays nothing -- the linker drops the module entirely. ==== One byte-stream API spans every port while the HAL hides very different hardware: the IIgs drives a Zilog 8530 SCC directly or a 6551 card in a slot; DOS uses an interrupt-driven 8250/16550 UART; the Amiga uses `serial.device`; the ST goes through the BIOS Aux channel. The model is *non-blocking and buffered*. Received bytes accumulate in a background ring -- a hardware interrupt on DOS, the OS on Amiga and ST, or `jlSerialPoll` on the IIgs -- and reads never block. Call `jlSerialPoll` once per frame: it is a no-op where the interrupt or OS already fills the ring, and the hardware drain where it does not, so calling it every frame is always correct. A failed `jlSerialOpen` is non-fatal: it returns `false` and every other call becomes a safe no-op, exactly like a failed audio init. === Types [source,c] ---- typedef enum { JL_SERIAL_DEFAULT = 0, // the platform's natural RS-232 port JL_SERIAL_MODEM, // IIgs modem port (SCC channel A) JL_SERIAL_PRINTER, // IIgs printer port (SCC channel B) JL_SERIAL_SLOT // card/port selected by config->unit } jlSerialDeviceE; typedef enum { JL_SERIAL_PARITY_NONE = 0, JL_SERIAL_PARITY_ODD, JL_SERIAL_PARITY_EVEN } jlSerialParityE; typedef enum { JL_SERIAL_FLOW_NONE = 0, // no handshaking JL_SERIAL_FLOW_RTSCTS, // hardware RTS/CTS JL_SERIAL_FLOW_XONXOFF // software XON/XOFF } jlSerialFlowE; typedef struct { uint32_t baud; // 300 .. 115200, clamped per port uint8_t dataBits; // 5 .. 8 uint8_t stopBits; // 1 or 2 jlSerialParityE parity; jlSerialFlowE flow; uint8_t unit; // JL_SERIAL_SLOT only } jlSerialConfigT; ---- The IIgs is the machine with three device choices; elsewhere `JL_SERIAL_MODEM` and `JL_SERIAL_PRINTER` both mean the default port. `JL_SERIAL_DEFAULT` selects the IIgs modem port, DOS COM1, the ST Modem port or Amiga unit 0. `unit` disambiguates `JL_SERIAL_SLOT` and is ignored otherwise: an IIgs Super Serial Card slot 1..7, a DOS COM number 1..4, or an Amiga `serial.device` unit. === jlSerialOpen [source,c] ---- bool jlSerialOpen(jlSerialDeviceE device, const jlSerialConfigT *config); ---- Opens and configures the port. Only one port may be open at a time; a second open without an intervening close fails. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`device` |`jlSerialDeviceE` |Which interface. |`config` |`const jlSerialConfigT *` |Line configuration. |=== *Returns:* `true` on success. `false` if the port cannot be opened or the platform has no serial support -- and then every other call no-ops. === jlSerialClose [source,c] ---- void jlSerialClose(void); ---- Closes the port and releases its resources. Safe when not open. *Parameters:* none. *Returns:* nothing. === jlSerialPoll [source,c] ---- void jlSerialPoll(void); ---- Pumps the receive and transmit buffers. Call once per game frame. *Parameters:* none. *Returns:* nothing. === jlSerialAvailable [source,c] ---- uint16_t jlSerialAvailable(void); ---- *Parameters:* none. *Returns:* the number of bytes waiting in the receive buffer, readable now without blocking. === jlSerialRead [source,c] ---- uint16_t jlSerialRead(uint8_t *buf, uint16_t max); ---- [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`buf` |`uint8_t *` |Destination buffer. |`max` |`uint16_t` |Buffer capacity. |=== *Returns:* the number of bytes copied, 0 to `max`. Never blocks. === jlSerialWrite [source,c] ---- uint16_t jlSerialWrite(const uint8_t *buf, uint16_t len); ---- Sends bytes. Transmit is a bounded poll. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`buf` |`const uint8_t *` |Data to send. |`len` |`uint16_t` |Byte count. |=== *Returns:* the number of bytes accepted, which may be fewer than `len` if a flow-controlled peer or a full transmit path stalls. === jlSerialWriteByte / jlSerialReadByte [source,c] ---- bool jlSerialWriteByte(uint8_t b); int16_t jlSerialReadByte(void); ---- Single-byte convenience calls. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`b` |`uint8_t` |Byte to send. |=== *Returns:* `jlSerialWriteByte` returns `true` if the byte was accepted. `jlSerialReadByte` returns the byte, or `-1` when none is ready. === jlSerialFlush [source,c] ---- void jlSerialFlush(void); ---- Discards any buffered received bytes. *Parameters:* none. *Returns:* nothing. === jlSerialSetRts [source,c] ---- void jlSerialSetRts(bool on); ---- Asserts or drops RTS, for a caller doing its own receive-side flow control. `JL_SERIAL_FLOW_RTSCTS` asks the *port* to handshake; this is the other half -- the application saying "my buffer is nearly full, stop sending", and later "go ahead". That is what a client with a small ring and a peer that can be told to pause actually needs. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`on` |`bool` |`true` asserts RTS, meaning ready to receive. |=== *Returns:* nothing. A port with no controllable RTS line no-ops, and a closed port no-ops; neither is an error, so this can be driven unconditionally.