179 lines
6.4 KiB
Bash
Executable file
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)"
|