183 lines
7.2 KiB
Bash
Executable file
183 lines
7.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
|
# See LICENSE.md for license information.
|
|
# SPDX-License-Identifier: MIT
|
|
#
|
|
# verify-x68000-tilebank.sh - Tile-bank gate for the X68000: proves that
|
|
# assetbake's x68000 encoder (plane-major, like the Amiga's, under its own
|
|
# target byte) writes exactly the layout the X68000's native tile paste
|
|
# (src/m68k/planarTile.h via src/x68000/x68kTile.h) reads, and that the
|
|
# X68000 build refuses a bank baked for any other target.
|
|
#
|
|
# Boots Human68k headless under MAME with TILEBANK.X (examples/tilebank) and
|
|
# the DATA/tilebank make/x68000.mk staged from the host gate's fixture on a
|
|
# data disk in drive B. The program loads its own bake through
|
|
# jlTileBankLoadToSurface and through jlTileBankLoad + jlTilePaste, checks
|
|
# every pixel of both against the PNG, hashes both, tries every other
|
|
# target's bake, logs one TILEBANK line and shows its verdict lamp. The Lua
|
|
# below waits for the lamp in text VRAM, then presses a key so the program
|
|
# exits -- Human68k only finalizes joeylog.txt's directory entry when the
|
|
# program's stdio closes it -- and stops MAME. The line, extracted from the
|
|
# data disk, must say PASS with both hashes equal to the host gate's
|
|
# (scripts/check-tilebank.sh, run here for the value) and every other target
|
|
# refused, and the lamp must be the PASS color.
|
|
#
|
|
# Text-plane geometry (see verify-x68000-input.sh): four 1bpp planes 0x20000
|
|
# apart at 0xE00000, 128 bytes per row, leftmost pixel in bit 7, the 320x200
|
|
# stage centered at (96, 156). The lamp is examples/tilebank's: 64x64 at stage
|
|
# (128, 68), color 1 for PASS and 2 for FAIL, with FILL_COLOR (9) either side
|
|
# of it on its rows.
|
|
#
|
|
# Usage: scripts/verify-x68000-tilebank.sh
|
|
# Env: TILEBANK_DATA_DIR stage this DATA/tilebank instead (negative controls)
|
|
# Requires: MAME, the X68000 firmware and CG ROM, a host cc;
|
|
# build/x68000/bin/TILEBANK.X and its DATA/tilebank (make x68000).
|
|
set -uo pipefail
|
|
|
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
source "$repo/scripts/lib/joeyEnv.sh"
|
|
source "$repo/scripts/lib/tileBankGate.sh"
|
|
joeyNeed MAME mame
|
|
joeyNeed X68K_DIR x68kFirmware
|
|
joeyNeedFile "$X68K_DIR/roms/x68000/cgrom.dat" x68kCgrom
|
|
source "$repo/scripts/lib/x68kDisk.sh"
|
|
joeyNeedFile "$X68K_SYSTEM_DISK" x68kFirmware
|
|
|
|
binDir=$repo/build/x68000/bin
|
|
dataDir=$(tileBankData "$binDir")
|
|
wall=600 # hard cap on MAME, whatever happens to this script
|
|
frames=20000 # emulated frames before the Lua gives up on the lamp
|
|
|
|
tileBankNeedBuild "$binDir/TILEBANK.X" "$dataDir" "make x68000"
|
|
tileBankReference
|
|
|
|
work=$(mktemp -d -t joeylib-x68-tilebank.XXXXXX)
|
|
trap 'rm -rf "$work"' EXIT
|
|
|
|
# The boot disk carries only Human68k and AUTOEXEC; the program, its DATA and
|
|
# its log live in GAME on the data disk in B:, and AUTOEXEC starts it from
|
|
# B:'s root -- so it must find its DATA/ from its process block, not the
|
|
# current directory, and log there too (src/core/fileRoot.h).
|
|
x68kBootDisk "$work/boot.xdf" 'B:' 'B:\GAME\TILEBANK.X' || exit 1
|
|
x68kDataDisk "$work/data.xdf" || exit 1
|
|
x68kXdf add "$work/data.xdf" "$binDir/TILEBANK.X" GAME/TILEBANK.X > /dev/null || exit 1
|
|
x68kAddTree "$work/data.xdf" "$dataDir" GAME/DATA/TILEBANK || exit 1
|
|
|
|
cat > "$work/tilebank.lua" <<LUA
|
|
local mem = manager.machine.devices[":maincpu"].spaces["program"]
|
|
local nat = manager.machine.natkeyboard
|
|
|
|
local FRAMES = $frames
|
|
local SAMPLE_EVERY = 10 -- frames between lamp looks
|
|
local LAMP_STABLE = 3 -- looks in a row that must agree
|
|
local KEY_EVERY = 60 -- frames between key presses once the lamp shows
|
|
local EXIT_SETTLE = 900 -- keypress -> jlShutdown -> stdio closes the log
|
|
|
|
local LAMP_X, LAMP_Y, LAMP_W, LAMP_H = 128, 68, 64, 64
|
|
local PASS_COLOR, FAIL_COLOR, FILL_COLOR = 1, 2, 9
|
|
local MARGIN = 4 -- inside probes keep off the lamp's edge
|
|
local BESIDE = 8 -- outside probes sit this far off it
|
|
|
|
local frame = 0
|
|
local seen = 0
|
|
local lamp = 0
|
|
local exitAt = 0
|
|
local done = false
|
|
|
|
-- The text plane holds two pages, and the port flips between them with the
|
|
-- text Y scroll (CRTC R11, which reads back): rows are counted from the page
|
|
-- on screen.
|
|
local function shownRow()
|
|
return mem:read_u16(0xE80016) & 0x3FF
|
|
end
|
|
|
|
local function stagePixel(px, py)
|
|
local x = 96 + px
|
|
local byteOff = (156 + shownRow() + py) * 128 + (x >> 3)
|
|
local bit = 0x80 >> (x & 7)
|
|
local v = 0
|
|
for plane = 0, 3 do
|
|
if (mem:read_u8(0xE00000 + plane * 0x20000 + byteOff) & bit) ~= 0 then
|
|
v = v | (1 << plane)
|
|
end
|
|
end
|
|
return v
|
|
end
|
|
|
|
-- The lamp color when the whole lamp shows one verdict color with
|
|
-- FILL_COLOR on its rows either side, else 0.
|
|
local function lampColor()
|
|
local left = LAMP_X + MARGIN
|
|
local right = LAMP_X + LAMP_W - 1 - MARGIN
|
|
local top = LAMP_Y + MARGIN
|
|
local bottom = LAMP_Y + LAMP_H - 1 - MARGIN
|
|
local midY = LAMP_Y + LAMP_H // 2
|
|
local c = stagePixel(left, top)
|
|
if c ~= PASS_COLOR and c ~= FAIL_COLOR then
|
|
return 0
|
|
end
|
|
for _, p in ipairs({ {right, top}, {left, bottom}, {right, bottom}, {LAMP_X + LAMP_W // 2, midY} }) do
|
|
if stagePixel(p[1], p[2]) ~= c then
|
|
return 0
|
|
end
|
|
end
|
|
if stagePixel(LAMP_X - BESIDE, midY) ~= FILL_COLOR or stagePixel(LAMP_X + LAMP_W - 1 + BESIDE, midY) ~= FILL_COLOR then
|
|
return 0
|
|
end
|
|
return c
|
|
end
|
|
|
|
emu.register_frame_done(function()
|
|
frame = frame + 1
|
|
if done then return end
|
|
if exitAt == 0 then
|
|
if frame % SAMPLE_EVERY == 0 then
|
|
local c = lampColor()
|
|
if c ~= 0 and c == lamp then
|
|
seen = seen + 1
|
|
else
|
|
seen = (c ~= 0) and 1 or 0
|
|
end
|
|
lamp = c
|
|
if seen >= LAMP_STABLE then
|
|
io.write(string.format("TILEBANK-X68K lamp=%s f%d\n", (lamp == PASS_COLOR) and "PASS" or "FAIL", frame))
|
|
io.flush()
|
|
exitAt = frame + EXIT_SETTLE
|
|
end
|
|
end
|
|
else
|
|
if frame % KEY_EVERY == 0 then nat:post(" ") end
|
|
if frame >= exitAt then
|
|
done = true
|
|
manager.machine:exit()
|
|
end
|
|
end
|
|
if frame > FRAMES then
|
|
done = true
|
|
io.write("TILEBANK-X68K no-lamp f"..frame.."\n")
|
|
io.flush()
|
|
manager.machine:exit()
|
|
end
|
|
end)
|
|
LUA
|
|
|
|
# From the work dir, so MAME's cfg/ and nvram/ never persist between runs.
|
|
out=$(cd "$work" && timeout -s KILL "$wall" "$MAME" x68000 -bios ipl10 \
|
|
-rompath "$X68K_DIR/roms" -flop1 "$work/boot.xdf" -flop2 "$work/data.xdf" \
|
|
-video none -sound none -nothrottle \
|
|
-autoboot_script "$work/tilebank.lua" < /dev/null 2>&1 | grep '^TILEBANK-X68K')
|
|
echo "${out:-TILEBANK-X68K: no report from the Lua (MAME died, or hit the ${wall}s cap)}"
|
|
|
|
lampOk=1
|
|
if ! grep -q 'lamp=PASS' <<< "$out"; then
|
|
lampOk=0
|
|
fi
|
|
if ! x68kXdf extract "$work/data.xdf" GAME/joeylog.txt "$work/joeylog.txt" > /dev/null 2>&1; then
|
|
echo "verify-x68000-tilebank: FAIL - no joeylog.txt on the data disk (the program never logged, or never exited)" >&2
|
|
exit 1
|
|
fi
|
|
tileBankJudge "$work/joeylog.txt" || exit 1
|
|
if [ "$lampOk" -eq 0 ]; then
|
|
echo "verify-x68000-tilebank: FAIL - the on-screen lamp did not show PASS" >&2
|
|
exit 1
|
|
fi
|