#!/usr/bin/env bash # probeHelloWindow.sh - headless run of demos/helloWindow that dumps # the step markers ($70..$72, written via MARK_AT = STA long, bank-0 # explicit) and the gDiag[] capture buffer (discovered from the .map) # so we can see what NewWindow ($090E) actually returns at the # toolbox-dispatcher level. # # Captures: # $70 = end-of-main marker ($99 = ran to completion) # $71 = step marker (last value reached: $10..$19) # $72 = NewWindow C-level result ($01/$02 = success, $EE = NULL) # gDiag[0/1] = paramLength readback ($4C if parm ptr is good) # gDiag[2/3] = result-space LOW word (window-ptr lo) # gDiag[4/5] = result-space HIGH word (window-ptr hi) # gDiag[6] = P byte at JSL return (bit 0 = carry) # gDiag[7] = $AA if probe ran to completion set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" OMF="$PROJECT_ROOT/demos/helloWindow.omf" SRC="$PROJECT_ROOT/demos/helloWindow.c" MAP="$PROJECT_ROOT/demos/helloWindow.map" if [ ! -f "$OMF" ] || [ "$SRC" -nt "$OMF" ]; then bash "$PROJECT_ROOT/demos/build.sh" helloWindow >/dev/null fi GDIAG_HEX=$(awk '$2 == "gDiag" {print $1; exit}' "$MAP") [ -n "$GDIAG_HEX" ] || { echo "gDiag not found in $MAP" >&2; exit 2; } GDIAG=$((GDIAG_HEX)) echo "gDiag at $(printf '0x%06x' $GDIAG)" CADIUS=${CADIUS:-$PROJECT_ROOT/tools/cadius/cadius} SYSDISK=${SYSDISK:-$PROJECT_ROOT/tools/gsos/sys602.po} [ -x "$CADIUS" ] || { echo "cadius not found: $CADIUS" >&2; exit 2; } [ -f "$SYSDISK" ] || { echo "sysdisk not found: $SYSDISK" >&2; exit 2; } command -v mame >/dev/null || { echo "mame not in PATH" >&2; exit 2; } WORK=$(mktemp -d -t probe.XXXXXX) trap 'rm -rf "$WORK"' EXIT cp "$SYSDISK" "$WORK/disk.po" "$CADIUS" CREATEVOLUME "$WORK/data.po" DATA 800KB >/dev/null cp "$OMF" "$WORK/HELLO#B30000" "$CADIUS" ADDFILE "$WORK/data.po" /DATA "$WORK/HELLO#B30000" >/dev/null cat > "$WORK/launch.lua" <> "$WORK/launch.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 get_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 = get_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 steps = { {3300, function() nat:post("D") end}, {3540, function() press(key_cmd) end}, {3546, function() nat:post("o") end}, {3600, function() release(key_cmd) end}, {4200, function() nat:post("H") end}, {4500, function() press(key_cmd) end}, {4506, function() nat:post("o") end}, {4560, function() release(key_cmd) end}, {7000, function() nat:post(" ") end}, {7500, function() nat:post(" ") end}, {8000, function() nat:post(" ") end}, {9000, function() for a = 0x70, 0x7f do print(string.format("MARK $%02x = %02x", a, mem:read_u8(a))) end print(string.format("MARK $96 (event type) = %02x", mem:read_u8(0x96))) print(string.format("MARK $97 (loop iters lo) = %02x", mem:read_u8(0x97))) print(string.format("MARK $93 (sizeof gWp) = %02x = %d", mem:read_u8(0x93), mem:read_u8(0x93))) print(string.format("A post-JSL = $%04x (lo=%02x hi=%02x)", mem:read_u8(0x90) | (mem:read_u8(0x91)*256), mem:read_u8(0x90), mem:read_u8(0x91))) local pbr = mem:read_u8(0x73) -- The OMF Loader places our segment at offset $0000 within bank -- $PBR (not at $1000 like our text-base). The cRELOC mechanism -- patches all symbolic abs-16 stores to (segPlacedBase + offsetRef) -- = ($0000 + symbol_offset_within_segment) = link_addr - text_base. -- So runtime_address(sym) = pbr*0x10000 + (link_addr - text_base). local TEXT_BASE = 0x1000 local base = pbr * 0x10000 + (GDIAG - TEXT_BASE) print(string.format("PBR = $%02x; gDiag at $%06x", pbr, base)) for i = 0, 15 do print(string.format("gDiag[%d] = %02x", i, mem:read_u8(base + i))) end -- Cross-check: read sanity bytes at the same offset in bank 0 -- and bank 8 to find where DBR-rel stores actually land. -- Search every bank from $00..$FF for $5A at offset $6F5D. for bank = 0, 0xff do local v = mem:read_u8(bank*0x10000+0x6F5D) if v == 0x5a then print(string.format("*** bank %02x HAS $5A at $6F5D ***", bank)) end end for _, bank in ipairs({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}) do print(string.format("bank %02x: $%06x = %02x (gDiag+e) $%06x = %02x (s2) $%06x = %02x (s1)", bank, bank*0x10000+0x6F5C, mem:read_u8(bank*0x10000+0x6F5C), bank*0x10000+0x6F5D, mem:read_u8(bank*0x10000+0x6F5D), bank*0x10000+0x6F4E, mem:read_u8(bank*0x10000+0x6F4E))) end -- Dump the runtime operand bytes of the symbolic `sta gDiag+0` -- at .bin offset $387. With text-base=$1000, that's runtime -- $08:$1387 (PBR + .bin offset + text-base). If the Loader -- re-applied the cRELOC with a different segPlacedBase, the -- operand bytes will differ from what the linker wrote. local probeBase = 0x081000 for ofs = 0x380, 0x39f, 0x10 do local row = string.format("$08:%04x:", ofs) for j = 0, 15 do row = row .. string.format(" %02x", mem:read_u8(probeBase + ofs - 0x1000 + j)) end print(row) end print(string.format("test $704E: $08=%02x $00=%02x", mem:read_u8(0x08704E), mem:read_u8(0x00704E))) print(string.format("test $7060: $08=%02x $00=%02x", mem:read_u8(0x087060), mem:read_u8(0x007060))) print(string.format("test $70FF: $08=%02x $00=%02x", mem:read_u8(0x0870FF), mem:read_u8(0x0070FF))) for b = 0, 0xff do if mem:read_u8(b*0x10000+0x704E) == 0xAB then print(string.format("*** $AB at bank %02x:$704e ***", b)) end end 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 OUT=$(timeout 220 mame apple2gs -rompath "$PROJECT_ROOT/tools/mame/roms" \ -window -nothrottle -sound none \ -seconds_to_run 180 -flop3 "$WORK/disk.po" -flop4 "$WORK/data.po" \ -autoboot_script "$WORK/launch.lua" &1) echo "$OUT" | grep -E "^(MARK|gDiag|bank|PBR|\*\*\*|\\\$08|test|A post)"