Displays position, speed, etc.
function num_stou(n, size)
if n < 0 then
n = n + 2^(8*size)
end
return n
end
function num_utos(n, size)
if n >= 2^(8*size-1) then
n = n - 2^(8*size)
end
return n
end
function numdec_s_le(buf, size)
local n = numdec_u_le(buf, size)
return num_utos(n, size)
end
function numdec_u_le(buf, size)
local n = 0
for i = 1, size do
n = n + 2^(8*(i-1)) * buf[i]
end
return n
end
function numdec_s_be(buf, size)
local n = numdec_u_be(buf, size)
return num_utos(n, size)
end
function numdec_u_be(buf, size)
local n = 0
for i = 1, size do
n = n + 2^(8*(size-i)) * buf[i]
end
return n
end
function numdec_s32_le(buf)
return numdec_s_le(buf, 4)
end
function numdec_u32_le(buf)
return numdec_u_le(buf, 4)
end
function numdec_s24_le(buf)
return numdec_s_le(buf, 3)
end
function numdec_u24_le(buf)
return numdec_u_le(buf, 3)
end
function numdec_s16_le(buf)
return numdec_s_le(buf, 2)
end
function numdec_u16_le(buf)
return numdec_u_le(buf, 2)
end
function numdec_s32_be(buf)
return numdec_s_be(buf, 4)
end
function numdec_u32_be(buf)
return numdec_u_be(buf, 4)
end
function numdec_s24_be(buf)
return numdec_s_be(buf, 3)
end
function numdec_u24_be(buf)
return numdec_u_be(buf, 3)
end
function numdec_s16_be(buf)
return numdec_s_be(buf, 2)
end
function numdec_u16_be(buf)
return numdec_u_be(buf, 2)
end
function mem_read_bytes(addr, len)
local buf = {}
for i = 1, len do
buf[i] = memory.readbyte(addr+(i-1))
end
return buf
end
function mem_read_s_le(addr, size)
local buf = mem_read_bytes(addr, size)
return numdec_s_le(buf, size)
end
function mem_read_u_le(addr, size)
local buf = mem_read_bytes(addr, size)
return numdec_u_le(buf, size)
end
function mem_read_s_be(addr, size)
local buf = mem_read_bytes(addr, size)
return numdec_s_be(buf, size)
end
function mem_read_u_be(addr, size)
local buf = mem_read_bytes(addr, size)
return numdec_u_be(buf, size)
end
function mem_read_s32_le(addr)
return mem_read_s_le(addr, 4)
end
function mem_read_u32_le(addr)
return mem_read_u_le(addr, 4)
end
function mem_read_s24_le(addr)
return mem_read_s_le(addr, 3)
end
function mem_read_u24_le(addr)
return mem_read_u_le(addr, 3)
end
function mem_read_s16_le(addr)
return mem_read_s_le(addr, 2)
end
function mem_read_u16_le(addr)
return mem_read_u_le(addr, 2)
end
function mem_read_s32_be(addr)
return mem_read_s_be(addr, 4)
end
function mem_read_u32_be(addr)
return mem_read_u_be(addr, 4)
end
function mem_read_s24_be(addr)
return mem_read_s_be(addr, 3)
end
function mem_read_u24_be(addr)
return mem_read_u_be(addr, 3)
end
function mem_read_s16_be(addr)
return mem_read_s_be(addr, 2)
end
function mem_read_u16_be(addr)
return mem_read_u_be(addr, 2)
end
function mem_read_s8(addr)
return num_utos(mem_read_u8(addr), 1)
end
function mem_read_u8(addr)
return memory.readbyte(addr)
end
local POS_CURB_LIMIT_MAX = 0x0061FF
local POS_CURB_LIMIT_MIN = 0xFFA200 - 0x1000000
local ENGINE_BRAKE = {
--LOW
{
{
0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0300, 0x0300, 0x0300, 0x0300, 0x0400, 0x0400, 0x0400, 0x0400,
0x0500, 0x0500, 0x0600, 0x0600, 0x0700, 0x0700, 0x0800, 0x0800,
},
{
0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0300, 0x0300, 0x0300, 0x0300, 0x0400, 0x0400, 0x0400, 0x0400,
0x0500, 0x0500, 0x0600, 0x0600, 0x0700, 0x0700, 0x0800, 0x0800,
},
},
--HI
{
{
0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
},
{
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
},
},
}
local ACCEL = {
-- LOW
{
{
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0300, 0x0300, 0x0300, 0x0280, 0x0240, 0x0220, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
},
{
0x0220, 0x0240, 0x0280, 0x0300, 0x0300, 0x0300, 0x0300, 0x0300,
0x0400, 0x0400, 0x0400, 0x0400, 0x0300, 0x0280, 0x0240, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
},
},
-- HI
{
{
0x0120, 0x0130, 0x0140, 0x0150, 0x0160, 0x0170, 0x0180, 0x0198,
0x01B0, 0x01C8, 0x01E0, 0x01F8, 0x0210, 0x0230, 0x0250, 0x0270,
0x0240, 0x0220, 0x02F0, 0x01C0, 0x0190, 0x0168, 0x0140, 0x0120,
0x0118, 0x0110, 0x0140, 0x0180, 0x0180, 0x0180, 0x0140, 0x00FF,
},
{
0x0240, 0x0260, 0x0280, 0x02A0, 0x02C0, 0x02E0, 0x0300, 0x0320,
0x0340, 0x0360, 0x0380, 0x03A0, 0x03B0, 0x03C0, 0x03D0, 0x03E0,
0x03C0, 0x03A0, 0x0380, 0x0360, 0x0340, 0x0320, 0x0300, 0x02E0,
0x0300, 0x0320, 0x0340, 0x0340, 0x0340, 0x0340, 0x0320, 0x00FF,
},
},
}
local CURVE_THRESHOLD = { 8, 24, 40 }
local CURVE_SPEED_LIMIT = {
-- LV1
{
{ 369, 339, 309 },
{ 509, 479, 449 },
},
-- LV2
{
{ 349, 319, 289 },
{ 489, 459, 429 },
},
-- LV3
{
{ 329, 299, 269 },
{ 469, 439, 409 },
},
}
local MOVE_MASK = { 0x80, 0x40, 0x40 }
local MOVE_STR = { "R", "L" }
local RAND_PERIOD = 0xFFFE
local rand_idx = {}
local function get_engine_brake(speed, gear, turbo)
local speed_idx = math.floor((speed/0x0100)/16) + 1
local gear_idx = gear+1
local turbo_idx = 1
if turbo then turbo_idx = turbo_idx+1; end
return ENGINE_BRAKE[gear_idx][turbo_idx][speed_idx]
end
local function get_accel(speed, gear, turbo)
local speed_idx = math.floor((speed/0x0100)/16) + 1
local gear_idx = gear+1
local turbo_idx = 1
if turbo then turbo_idx = turbo_idx+1; end
return ACCEL[gear_idx][turbo_idx][speed_idx]
end
local function get_speed_limit(level, curve, turbo)
local curve_abs = math.abs(curve)
local curve_idx = 0
for i = 1, #CURVE_THRESHOLD do
if curve_abs >= CURVE_THRESHOLD[i] then
curve_idx = curve_idx+1
end
end
if curve_idx == 0 then return nil; end
local level_idx = level+1
local turbo_idx = 1
if turbo then turbo_idx = turbo_idx+1; end
return CURVE_SPEED_LIMIT[level_idx][turbo_idx][curve_idx]
end
local function get_move_dir(move, level)
local mask = MOVE_MASK[level+1]
if bit.band(move, mask) == 0 then
return 1 -- R
else
return 2 -- L
end
end
local function chk_enemy_pop(rand1, speed)
if speed >= 0xC800 then
return bit.band(rand1, 0x1F) == 0
else
return bit.band(rand1, 0x01FF) == 0
end
end
local function rand_shift(r)
local bit_new = bit.band(bit.bnot(bit.bxor(bit.rshift(r,15), bit.rshift(r,1), r)), 0x0001)
return bit.band(bit.bor(bit.lshift(r,1), bit_new), 0xFFFF)
end
local function init_rand_idx()
local r = 0x0000
for i = 1, RAND_PERIOD do
rand_idx[r] = i
r = rand_shift(r)
end
end
local function get_info()
local info = {}
info.level = mem_read_u8(0x33)
info.frame = mem_read_u8(0x4F)
info.gear = mem_read_u8(0x47)
info.turbo = mem_read_u8(0x75) ~= 0
local pos_buf = { mem_read_u8(0x4D), mem_read_u8(0x4C), mem_read_u8(0x4E) }
info.pos = numdec_s24_be(pos_buf)
info.speed = mem_read_u24_be(0x50)
info.rpm = mem_read_u8(0x53)
info.distance = mem_read_u16_le(0x41)
info.distance_way = mem_read_u8(0x43)
local way_ptr = mem_read_u16_le(0x3C)
info.way_curve = mem_read_s8(way_ptr)
info.way_len = mem_read_u8(way_ptr+1)
info.enemy = {}
for i = 1, 3 do
local e = {}
e.x = mem_read_u8(0x8C+i-1)
e.z = 256*mem_read_u8(0x8F+i-1) + mem_read_u8(0x92+i-1)
e.move = mem_read_u8(0x98+i-1)
info.enemy[i] = e
end
info.rand0 = mem_read_u16_le(0x2E)
info.rand1 = mem_read_u16_le(0x30)
return info
end
local function color_curb(frame)
return frame % 2 == 1 and "red" or "gray"
end
local function color_speed(frame)
return frame % 2 == 1 and "gray" or "white"
end
local function draw_info(info)
local str_pos = string.format("%.3f", info.pos/256.0, num_stou(info.pos, 3))
local curb_hit = info.pos < POS_CURB_LIMIT_MIN or info.pos > POS_CURB_LIMIT_MAX
local engine_brake = get_engine_brake(info.speed, info.gear, info.turbo)
local accel = get_accel(info.speed, info.gear, info.turbo)
local str_speed = string.format("%.3f", info.speed/256.0)
local str_dspeed = string.format("%.3f-%.3f=%.3f",
accel/256.0, engine_brake/256.0, (accel-engine_brake)/256.0)
local speed_limit = get_speed_limit(info.level, info.way_curve, info.turbo)
local str_way = string.format("WAY: %d, %u/%u", info.way_curve, info.distance_way, info.way_len)
if speed_limit then
str_way = str_way .. string.format(", lim:%03u", speed_limit)
end
local str_enemy = "ENM: "
for i = 1, 3 do
local e = info.enemy[i]
local x_str, z_str = "----", "------"
if e.z < 0xF000 then
x_str = string.format("0x%02X", e.x)
z_str = string.format("0x%04X", e.z)
end
str_enemy = str_enemy .. string.format("(%s,%s)", x_str, z_str)
if i ~= 3 then str_enemy = str_enemy .. " "; end
end
local str_enemy_act = "ACT: "
for i = 1, 3 do
local e = info.enemy[i]
local move_str = string.format("0x%02X(%s)", e.move, MOVE_STR[get_move_dir(e.move, info.level)])
str_enemy_act = str_enemy_act .. move_str
if i ~= 3 then str_enemy_act = str_enemy_act .. " "; end
end
local rand1_str = string.format("rand1:%d", rand_idx[info.rand1])
if chk_enemy_pop(info.rand1, info.speed) then
rand1_str = rand1_str .. "(!)"
end
str_enemy_act = str_enemy_act .. " " .. rand1_str
gui.text(0, 48, "POS:")
gui.text(23, 48, str_pos)
if curb_hit then
gui.text(80, 48, "curb!", color_curb(info.frame))
end
gui.text(0, 56, "SPD:")
gui.text(22, 56, str_speed, color_speed(info.frame))
gui.text(80, 56, str_dspeed)
gui.text(0, 64, string.format("RPM: %u", info.rpm))
gui.text(0, 80, string.format("DIS: %.3f", info.distance/256.0, info.distance))
gui.text(0, 88, str_way)
gui.text(0, 104, str_enemy)
gui.text(0, 112, str_enemy_act)
gui.text(0, 8, string.format("%03u", info.frame))
end
local function draw()
local info = get_info()
-- �n�[�h���Z�b�g����̓�����������������ĂȂ��̂ŏ��\�����Ȃ�
if mem_read_u8(0x0600) == 0xA5 then
draw_info(info)
end
end
local function main()
init_rand_idx()
emu.registerafter(draw)
end
main()