#!/usr/bin/env bash
# verify-iigs-serial.sh - Cross-channel SCC gate for the Apple IIgs port.
#
# The 8530 carries both built-in ports on one chip: channel A is the printer
# port (AppleTalk's), channel B is the modem port. WR9's reset command is
# chip-wide, so an open that issues the force-hardware-reset (0xC0) wipes the
# other channel too. This gate proves it does not.
#
# SCCPROBE (examples/sccprobe/sccprobe.c) brings channel A up through the real
# HAL and sends "A1", hands it back, opens channel B -- the operation under
# test -- and then pokes "A2" straight into channel A's data register. Both
# strings reach the host only if channel A's transmitter is still enabled after
# the modem-port open, so:
#
#   A1 present, A2 present  -> PASS  (channel A survived)
#   A1 present, A2 missing  -> FAIL  (the open wiped the other channel)
#   A1 missing              -> the probe never ran / printer path is broken
#
# MAME CONNECTS OUT to the bitbanger socket, so the peer here listens first.
# Needs the PATCHED apple2gs (patches/mame-0.264-apple2gs-scc-clock.patch):
# stock 0.264 feeds the SCC BRG the wrong clock and pins RX at 230400, so no
# baud ever matches the null_modem.
#
#   scripts/verify-iigs-serial.sh
set -uo pipefail

repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
MAME="$repo/toolchains/cache/mame-mame0264/apple2gs"
sys_disk=$repo/toolchains/emulators/support/gsos-system.po
rompath="${MAME_ROMPATH:-$HOME/.mame/roms}"
PORT="${IIGS_SCC_PORT:-45231}"
readFrame="${MAME_READ_FRAME:-9000}"
WALL="${IIGS_SCC_WALL:-420}"

[ -x "$MAME" ] || { echo "verify-iigs-serial: missing patched MAME at $MAME" >&2; exit 2; }
[ -f "$sys_disk" ] || { echo "verify-iigs-serial: missing $sys_disk" >&2; exit 2; }
[ -f "$repo/build/iigs/bin/SCCPROBE" ] || { echo "verify-iigs-serial: build SCCPROBE first" >&2; exit 2; }

work=$(mktemp -d -t joeylib-iigs-scc.XXXXXX)
peer_pid=""
cleanup() {
    [ -n "$peer_pid" ] && kill -9 "$peer_pid" 2>/dev/null
    rm -rf "$work"
}
trap cleanup EXIT

# A disk holding just the probe, so this never disturbs the normal joey.2mg.
JOEY_DISK_EXAMPLES="SCCPROBE" bash "$repo/scripts/make-iigs-disk.sh" "$work/scc.2mg" >/dev/null 2>&1 || {
    echo "verify-iigs-serial: FAIL - could not build the probe disk" >&2
    exit 1
}
cp "$sys_disk" "$work/boot.po"

# Serial peer: listen, then log every byte the IIgs sends.
cat > "$work/peer.py" <<'PY'
import socket, sys, time

port = int(sys.argv[1])
out  = sys.argv[2]
srv  = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("127.0.0.1", port))
srv.listen(1)
srv.settimeout(300)
try:
    conn, _ = srv.accept()
except socket.timeout:
    open(out, "wb").write(b"")
    sys.exit(0)
conn.settimeout(1.0)
buf = b""
deadline = time.time() + 280
while time.time() < deadline:
    try:
        chunk = conn.recv(256)
    except socket.timeout:
        continue
    except OSError:
        break
    if not chunk:
        break
    buf += chunk
    with open(out, "wb") as f:
        f.write(buf)
with open(out, "wb") as f:
    f.write(buf)
PY
python3 "$work/peer.py" "$PORT" "$work/rx.bin" &
peer_pid=$!
sleep 1

# Finder keystroke timeline, same calibration as verify-iigs.sh: select the
# JOEYLIB volume, Cmd-O to open it, type the program name, Cmd-O to launch.
cat > "$work/scc.lua" <<LUA
local cpu = manager.machine.devices[":maincpu"]
local mem = cpu.spaces["program"]
local nat = manager.machine.natkeyboard
local frame = 0
local idx = 1

local function field(port, name)
    local p = manager.machine.ioport.ports[port]
    if p == nil then return nil end
    return p.fields[name]
end
local key_cmd = field(":macadb:KEY3", "Command / Open Apple")
local function press(f)   if f then f:set_value(1) end end
local function release(f) if f then f:set_value(0) end end

local function report()
    -- Probe mailbox, stamped into the SHR framebuffer (it never calls jlInit,
    -- so nothing else writes here): signature, then one flag per step.
    local base = 0xE12000
    io.write(string.format("VERIFY-IIGS-SCC frame=%d sig=%02X%02X printerOpen=%d printerWrote=%d modemOpen=%d directWrote=%d\n",
                           frame,
                           mem:read_u8(base), mem:read_u8(base + 1),
                           mem:read_u8(base + 2), mem:read_u8(base + 3),
                           mem:read_u8(base + 4), mem:read_u8(base + 5)))
    io.flush()
end

local steps = {
    {3000, function() nat:post("J") end},
    {3120, function() press(key_cmd) end},
    {3126, function() nat:post("o") end},
    {3180, function() release(key_cmd) end},
    {3540, function() nat:post("SCCPROBE") end},
    {3660, function() press(key_cmd) end},
    {3666, function() nat:post("o") end},
    {3720, function() release(key_cmd) end},
    {$readFrame, function() report(); manager.machine:exit() end},
}

emu.register_frame_done(function()
    frame = frame + 1
    while idx <= #steps and frame >= steps[idx][1] do
        steps[idx][2]()
        idx = idx + 1
    end
end)
LUA

cd "$work"
out=$(QT_QPA_PLATFORM=offscreen SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
    timeout -s KILL "$WALL" "$MAME" apple2gs \
        -rompath "$rompath" \
        -flop3 "$work/boot.po" -flop4 "$work/scc.2mg" \
        -printer null_modem -bitb "socket.127.0.0.1:$PORT" \
        -video none -sound none -nothrottle \
        -autoboot_script "$work/scc.lua" </dev/null 2>&1) || true

kill -9 "$peer_pid" 2>/dev/null
peer_pid=""
sleep 1

line=$(echo "$out" | grep -E '^VERIFY-IIGS-SCC ' | tail -1)
rx=$(od -A n -c "$work/rx.bin" 2>/dev/null | tr -s ' ' | tr -d '\n')
echo "$line"
echo "  printer-port bytes:$rx"

if [ -z "$line" ]; then
    echo "verify-iigs-serial: FAIL - MAME produced no report" >&2
    echo "$out" | tail -10 >&2
    exit 1
fi
if ! grep -q "A1" "$work/rx.bin" 2>/dev/null; then
    echo "verify-iigs-serial: FAIL - the printer port never sent 'A1'." >&2
    echo "  Either SCCPROBE did not run (check the flags above) or the" >&2
    echo "  channel A transmit path is broken." >&2
    exit 1
fi
if ! grep -q "A2" "$work/rx.bin" 2>/dev/null; then
    echo "verify-iigs-serial: FAIL - 'A1' arrived but 'A2' did not." >&2
    echo "  Opening the MODEM port killed the PRINTER channel: that is the" >&2
    echo "  chip-wide WR9 0xC0 force-hardware-reset. Use the per-channel" >&2
    echo "  reset (0x80 channel A / 0x40 channel B) in serialInitScc." >&2
    exit 1
fi
echo "verify-iigs-serial: PASS (printer channel still transmits after the modem port was opened)"
