joeylib2/scripts/verify-iigs-serial.sh

179 lines
6.4 KiB
Bash
Executable file

#!/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)"