// Sharp X68000 serial HAL: RS-232C via the IOCS polled API.
//
// Structurally the Atari ST HAL (src/atarist/serial.c): ride the OS's polled
// interface, hijack no vector, buffer nothing ourselves. The IOCS calls map
// one-for-one onto the ST's XBIOS ones --
//     _iocs_set232c   <- Rsconf     (baud + framing, one packed word)
//     _iocs_isns232c  <- Bconstat   (input status)
//     _iocs_inp232c   <- Bconin     (read one byte, BLOCKS if empty)
//     _iocs_osns232c  <- Bcostat    (transmitter ready)
//     _iocs_out232c   <- Bconout    (write one byte)
//
// The machine has one RS-232C line (SCC channel A); channel B is the mouse and
// is not reachable here, so jlSerialDeviceE is accepted and ignored exactly as
// on the ST.

#include <x68k/iocs.h>

#include "port.h"


// LINE CONFIGURATION IS DELIBERATELY NOT APPLIED YET.
//
// _iocs_set232c takes a single packed mode word, and the packing could not be
// confirmed. What IS confirmed, from XEiJ's RS232CTerminal.java (which reads
// the live mode word out of the RSDRV.SYS work area):
//     bit 7  RTS/CTS flow control
//     bit 9  XON/XOFF flow control
// and its baud-rate table is
//     0=75 1=150 2=300 3=600 4=1200 5=2400 6=4800 7=9600 8=19200 9=31250
//     10=38400 11=50000 12=57600 13=76800 14=115200 15=230400
// i.e. the rate is a FOUR-bit field, so it cannot sit at bits 0-2 with parity
// at bit 3. The positions of the data/stop/parity fields remain unknown.
//
// Writing a guessed mode word is worse than writing none: a wrong framing
// setting still transmits, it just corrupts every byte, which is exactly the
// failure this port hit first time out (every received byte came back as 0x00
// or 0x80 -- only the MSB surviving).
//
// So jlpSerialOpen inherits whatever RSDRV.SYS configured at boot. The Human68k
// "RS-232C DRIVER for X68000 version 2.02" banner appears during startup and
// leaves the port at its default, which pairs with MAME's null_modem default
// of 9600 8N1. Honour jlSerialConfigT properly once the layout is confirmed
// against hardware documentation.

// Bounded transmitter-ready spin, so a stalled link (no CTS) reports a partial
// write instead of hanging the frame. Same guard the ST HAL uses.
#define X68K_TX_SPIN_LIMIT      200000ul


// ----- HAL entry points (alphabetical) -----

uint16_t jlpSerialAvailable(void) {
    // _iocs_isns232c reports presence, not a count: non-zero == at least one.
    return (_iocs_isns232c() != 0) ? 1u : 0u;
}


void jlpSerialClose(void) {
    // IOCS path hijacks no vector -- nothing to tear down.
}


void jlpSerialFlush(void) {
    while (_iocs_isns232c() != 0) {
        (void)_iocs_inp232c();
    }
}


bool jlpSerialOpen(jlSerialDeviceE device, const jlSerialConfigT *config) {
    (void)device;   // one RS-232C line; channel B is the mouse.
    if (config == NULL) {
        return false;
    }
    // config is accepted but NOT applied -- see the mode-word note above. The
    // port keeps whatever RSDRV.SYS set at boot.
    (void)config;
    return true;
}


void jlpSerialPoll(void) {
    // IOCS buffers RX itself; nothing to pump.
}


uint16_t jlpSerialRead(uint8_t *buf, uint16_t max) {
    uint16_t n;

    n = 0u;
    // Gate each read on the status call first: _iocs_inp232c BLOCKS when the
    // buffer is empty, which would stall the frame.
    while (n < max && _iocs_isns232c() != 0) {
        buf[n] = (uint8_t)(_iocs_inp232c() & 0xFF);
        n++;
    }
    return n;
}


uint16_t jlpSerialWrite(const uint8_t *buf, uint16_t len) {
    uint16_t n;
    uint32_t guard;

    for (n = 0u; n < len; n++) {
        guard = 0ul;
        while (_iocs_osns232c() == 0) {
            guard++;
            if (guard > X68K_TX_SPIN_LIMIT) {
                return n;
            }
        }
        _iocs_out232c((int)buf[n]);
    }
    return n;
}
