mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-11-01 12:14:56 +02:00
196 lines
5.9 KiB
Lua
196 lines
5.9 KiB
Lua
-- load_mame.lua -- debugger entry for the Bad Apple payload.
|
|
--
|
|
-- Loads the payload at bit 0x018C0000 and the movie at bit 0x00080000, then
|
|
-- enters the payload at the OS idle loop. This is the development path; the
|
|
-- real delivery path is tools/load.sh (rdebug + LANCE).
|
|
--
|
|
-- Env:
|
|
-- BA_MODE=one|play (one is the only mode implemented here)
|
|
-- BA_MOVIE movie file (default bad-apple/data/test_movie.bin)
|
|
-- BA_PAYLOAD raw payload (default bad-apple/player/build/player.bin)
|
|
-- BA_ENV player.env with ENTRY_PTR (default .../player.env)
|
|
-- BA_AT load frame (default 2500)
|
|
-- BA_END inspection/exit frame (default 2600)
|
|
|
|
local function hex(v, w)
|
|
return string.format("%0" .. (w or 8) .. "X", v)
|
|
end
|
|
|
|
local machine = manager.machine
|
|
local cpu = machine.devices[':maincpu']
|
|
local dbg = machine.debugger
|
|
local prog = cpu.spaces['program']
|
|
|
|
if not dbg then
|
|
print("BA ERROR: debugger not enabled; pass -debug")
|
|
machine:exit()
|
|
return
|
|
end
|
|
|
|
local ORIGIN = 0x018C0000
|
|
local MOVIE = tonumber(os.getenv("BA_MOVIE_BASE") or "0x01D00000")
|
|
local MARK = 0x01810000
|
|
local ENTRY_PC = 0x3F579970
|
|
local AT = tonumber(os.getenv("BA_AT") or "2500")
|
|
local STOP_AT = tonumber(os.getenv("BA_END") or "2600")
|
|
local START = tonumber(os.getenv("BA_START") or "0")
|
|
local COUNT = tonumber(os.getenv("BA_COUNT") or "0")
|
|
local PACE = tonumber(os.getenv("BA_PACE") or "0")
|
|
local WAIT = tonumber(os.getenv("BA_WAIT") or "0")
|
|
local MOVIE_PATH = os.getenv("BA_MOVIE") or "bad-apple/data/test_movie.bin"
|
|
local PAYLOAD_PATH = os.getenv("BA_PAYLOAD") or "bad-apple/player/build/player.bin"
|
|
local ENV_PATH = os.getenv("BA_ENV") or "bad-apple/player/build/player.env"
|
|
|
|
local function read_all(path)
|
|
local f, err = io.open(path, "rb")
|
|
if not f then
|
|
print("BA ERROR: cannot open " .. path .. ": " .. tostring(err))
|
|
machine:exit()
|
|
return nil
|
|
end
|
|
local data = f:read("*a")
|
|
f:close()
|
|
return data
|
|
end
|
|
|
|
local function entry_from_env()
|
|
local explicit = os.getenv("BA_ENTRY")
|
|
if explicit then
|
|
return tonumber(explicit)
|
|
end
|
|
local f = io.open(ENV_PATH, "r")
|
|
if not f then
|
|
return ORIGIN
|
|
end
|
|
for line in f:lines() do
|
|
local value = line:match("ENTRY_PTR=0x(%x+)")
|
|
if value then
|
|
f:close()
|
|
return tonumber(value, 16)
|
|
end
|
|
end
|
|
f:close()
|
|
return ORIGIN
|
|
end
|
|
|
|
local payload = read_all(PAYLOAD_PATH)
|
|
local movie = read_all(MOVIE_PATH)
|
|
if not payload or not movie then
|
|
return
|
|
end
|
|
local entry = entry_from_env()
|
|
print(string.format("BA: payload %d bytes, movie %d bytes, entry %s",
|
|
#payload, #movie, hex(entry)))
|
|
|
|
local function write_bytes(data, base)
|
|
local i
|
|
local n = #data
|
|
i = 0
|
|
while i + 4 <= n do
|
|
local b0 = data:byte(i + 1)
|
|
local b1 = data:byte(i + 2)
|
|
local b2 = data:byte(i + 3)
|
|
local b3 = data:byte(i + 4)
|
|
local word = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)
|
|
prog:write_u32(base + i * 8, word)
|
|
i = i + 4
|
|
end
|
|
while i < n do
|
|
prog:write_u8(base + i * 8, data:byte(i + 1))
|
|
i = i + 1
|
|
end
|
|
end
|
|
|
|
local function verify(data, base)
|
|
local i
|
|
for i = 0, #data - 1 do
|
|
if prog:read_u8(base + i * 8) ~= data:byte(i + 1) then
|
|
return false, i
|
|
end
|
|
end
|
|
return true, 0
|
|
end
|
|
|
|
local loaded = false
|
|
local entered = false
|
|
local done = false
|
|
local movie_sent = false
|
|
local frames = 0
|
|
|
|
local function load_and_enter()
|
|
write_bytes(payload, ORIGIN)
|
|
local ok, bad = verify(payload, ORIGIN)
|
|
if not ok then
|
|
print("BA ERROR: payload readback mismatch at byte " .. bad)
|
|
machine:exit()
|
|
return
|
|
end
|
|
prog:write_u32(MARK + 0x20 * 8, START)
|
|
prog:write_u32(MARK + 0x24 * 8, COUNT)
|
|
prog:write_u32(MARK + 0x28 * 8, PACE)
|
|
prog:write_u32(MARK + 0x2C * 8, MOVIE)
|
|
prog:write_u32(MARK + 0x30 * 8, #movie)
|
|
prog:write_u32(MARK + 0x34 * 8, WAIT)
|
|
prog:write_u32(MARK + 0x38 * 8, 0)
|
|
prog:write_u32(MARK + 0x3C * 8, 0) -- debugger path, never network
|
|
dbg:command("bpset " .. hex(ENTRY_PC) .. ",,{ pc = " .. hex(entry) .. " ; g }")
|
|
if WAIT == 0 then
|
|
write_bytes(movie, MOVIE)
|
|
ok, bad = verify(movie, MOVIE)
|
|
if not ok then
|
|
print("BA ERROR: movie readback mismatch at byte " .. bad)
|
|
machine:exit()
|
|
return
|
|
end
|
|
end
|
|
print(string.format("BA: loaded; start=%d count=%d pace=%d wait=%d",
|
|
START, COUNT, PACE, WAIT))
|
|
end
|
|
|
|
emu.register_frame_done(function()
|
|
frames = frames + 1
|
|
if frames == AT and not loaded then
|
|
loaded = true
|
|
load_and_enter()
|
|
end
|
|
if loaded and WAIT == 1 and not movie_sent
|
|
and prog:read_u16(MARK + 0x10 * 8) == 0x10 then
|
|
movie_sent = true
|
|
print("BA: takeover seen; sending movie to low RAM")
|
|
write_bytes(movie, MOVIE)
|
|
local ok, bad = verify(movie, MOVIE)
|
|
if not ok then
|
|
print("BA ERROR: movie readback mismatch at byte " .. bad)
|
|
machine:exit()
|
|
return
|
|
end
|
|
prog:write_u32(MARK + 0x38 * 8, 1)
|
|
print("BA: movie ready")
|
|
end
|
|
if loaded and not entered then
|
|
local status = prog:read_u16(MARK + 0x10 * 8)
|
|
if status == 1 or status == 2 then
|
|
entered = true
|
|
done = true
|
|
print(string.format("BA: STATUS=%d at frame %d; FRAME=%d",
|
|
status, frames, prog:read_u16(MARK)))
|
|
end
|
|
end
|
|
if loaded and not done and frames > AT + 10 then
|
|
local err = prog:read_u32(MARK)
|
|
if err >= 0xBAD00000 then
|
|
print(string.format("BA ERROR: marker %s at frame %d",
|
|
hex(err), frames))
|
|
machine:exit()
|
|
end
|
|
end
|
|
if frames >= STOP_AT then
|
|
print(string.format("BA: end at frame %d (loaded=%s done=%s)",
|
|
frames, tostring(loaded), tostring(done)))
|
|
machine:exit()
|
|
end
|
|
end, "vt1200-badapple-loader")
|
|
|
|
dbg.visible_cpu = cpu
|
|
dbg.execution_state = 'run'
|