// Serial communications add-on -- portable RS-232 byte streams. // // OPTIONAL add-on: this header is NOT pulled in by . Include it // explicitly. An app that never references jlSerial* pays nothing -- the // linker drops the module (archive granularity / IIgs dead-strip). // // One byte-stream API spans every port; the per-port HAL hides the very // different hardware: // Apple IIgs -- direct Zilog 8530 SCC (modem/printer ports) or a 6551 // Super Serial Card in a slot; its own RX interrupt fills // the ring (a card needs its interrupt switch, SW2-6, on). // DOS -- 8250/16550 UART on a COM port; interrupt-driven RX ring. // Amiga -- serial.device (async exec IO); the OS buffers RX. // Atari ST -- BIOS Aux (Modem port) via XBIOS Rsconf; TOS buffers RX. // X68000 -- SCC channel A: IOCS for transmit and line setup (75-9600 // bps, no RTS/CTS; 75-38400 bps and RTS/CTS with Human68k's // RSDRV.SYS loaded); its own RX interrupt fills the ring. // // Model: non-blocking + buffered. Received bytes accumulate in a background // ring (a hardware IRQ on DOS, the X68000 and the IIgs, the OS on Amiga/ST); // jlSerialRead / jlSerialAvailable never block. Call jlSerialPoll() // once per game frame -- it is a no-op where the IRQ/OS already fills the // ring, and the hardware drain where it does not, so calling it every frame is // always correct. Transmit is a bounded poll. // // A failed jlSerialOpen is non-fatal: it returns false and every other call is // a safe no-op (Available 0, Read 0), exactly like a failed audio init. #ifndef JOEYLIB_SERIAL_H #define JOEYLIB_SERIAL_H #include "platform.h" #include "types.h" // Which physical interface to open. The IIgs is the oddball with three: // JL_SERIAL_MODEM -- IIgs modem port (SCC channel B). Elsewhere == DEFAULT. // JL_SERIAL_PRINTER -- IIgs printer port (SCC channel A). Only the IIgs has // a second built-in port; elsewhere == DEFAULT. // JL_SERIAL_SLOT -- a card/port selected by jlSerialConfigT.unit: IIgs // Super Serial Card slot (1..7); DOS COM number (1..4); // Amiga serial.device unit. // JL_SERIAL_DEFAULT -- the platform's natural RS-232 port (IIgs modem, DOS // COM1, ST Modem port, Amiga unit 0). typedef enum { JL_SERIAL_DEFAULT = 0, JL_SERIAL_MODEM, JL_SERIAL_PRINTER, JL_SERIAL_SLOT } 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; // Line configuration. `unit` disambiguates JL_SERIAL_SLOT (see jlSerialDeviceE) // and is ignored for the other devices. typedef struct { uint32_t baud; // 300 .. 115200 (each port clamps to what it supports) uint8_t dataBits; // 5 .. 8 uint8_t stopBits; // 1 or 2 jlSerialParityE parity; jlSerialFlowE flow; uint8_t unit; // JL_SERIAL_SLOT: IIgs slot 1..7 / DOS COM 1..4 / Amiga unit } jlSerialConfigT; // Open + configure the port. Only one port is open at a time; a second // jlSerialOpen without a jlSerialClose fails. Returns false (non-fatal) if the // port cannot be opened or the platform has no serial support -- every other // call then no-ops. bool jlSerialOpen(jlSerialDeviceE device, const jlSerialConfigT *config); // Close the open port and release its resources. Safe to call when not open. // A port still open when the program exits is closed then (the first // successful jlSerialOpen registers an atexit handler): where the port's RX // interrupt is taken over (DOS, X68000, IIgs), the vector points into the // program until it is closed. // // Closing does NOT hang up: DTR is left exactly as it was, so a modem call or a // BBS caller survives a program handing the line back -- a door returning to // its BBS, a terminal exiting. RTS is not part of that promise (on the Amiga, // serial.device puts back what it found at open). To hang up, drop DTR with // jlSerialSetDtr(false), wait longer than the modem's DTR-drop detection time // (Hayes S25: 50 ms by default), then close: the line stays down. void jlSerialClose(void); // Pump the RX/TX buffers. Call once per game frame. Next to a no-op where a // hardware IRQ or the OS already services the port; drains the hardware into // the RX ring where nothing else does (an IIgs Super Serial Card whose // interrupt switch is off). Calling it every frame is always correct. void jlSerialPoll(void); // Bytes waiting in the RX buffer, readable now without blocking. uint16_t jlSerialAvailable(void); // Copy up to `max` received bytes into `buf`; returns the count copied // (0 .. max). Never blocks. uint16_t jlSerialRead(uint8_t *buf, uint16_t max); // Send `len` bytes; returns the count accepted (may be < len if a flow- // controlled peer or a full transmit path stalls). Transmit is a bounded poll. uint16_t jlSerialWrite(const uint8_t *buf, uint16_t len); // Single-byte convenience. jlSerialReadByte returns -1 when none is ready. bool jlSerialWriteByte(uint8_t b); int16_t jlSerialReadByte(void); // Discard any buffered received bytes. void jlSerialFlush(void); // RAISE OR DROP DTR -- how a program hangs up. // // jlSerialOpen raises DTR and jlSerialClose leaves it as it is, so this is the one call that drops // it. To hang up a modem: jlSerialSetDtr(false), wait longer than the modem's DTR-drop detection // time (Hayes S25: 50 ms by default), then jlSerialClose -- the line stays down. `on` = true raises // it again. // // Two ports tie DTR to more than the line. An IIgs Super Serial Card's 6551 has no separate DTR // bit: dropping DTR also stops the card's receiver until DTR is raised again. On the X68000 the // line setup in the ROM and in RSDRV.SYS raises DTR whenever it runs, so the next jlSerialOpen // (or anything else that sets the line) raises it again. A closed port no-ops, so a caller can // drive it unconditionally. void jlSerialSetDtr(bool on); // ASSERT OR DROP RTS, for a caller that does 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", which is what a client with a small // ring and a peer that can be told to pause actually needs. RetroNet's 6551 clients (C64, Apple II) // have done exactly this for a long time and the 16-bit tier could not, so a JoeyLib target's only // defence against an overrun was the packet layer's retransmit. // // `on` = true asserts RTS (ready to receive). A port with no controllable RTS line no-ops, and a // closed port no-ops; neither is an error, so a caller can drive it unconditionally. void jlSerialSetRts(bool on); #endif