-- 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'