#!/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" <<LUA
local GDIAG = $GDIAG
LUA
cat >> "$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" </dev/null 2>&1)

echo "$OUT" | grep -E "^(MARK|gDiag|bank|PBR|\*\*\*|\\\$08|test|A post)"
