Merge branch 'master' into 'master'

Add support for reading internal SNES Header, limited autoconfiguration (HiROM v LoROM).

See merge request InfiniteNesLives/INL-retro-progdump!21
This commit is contained in:
Paul Molloy 2019-02-11 19:55:50 +00:00
commit 1d9aa2f00f
1 changed files with 353 additions and 7 deletions

View File

@ -10,8 +10,339 @@ local snes = require "scripts.app.snes"
local apperase = require "scripts.app.erase"
-- file constants
local hirom_name = 'hirom'
local lorom_name = 'lorom'
local hirom_name = 'HiROM'
local lorom_name = 'LoROM'
-- Useful References:
-- http://old.smwiki.net/wiki/Internal_ROM_Header
-- https://en.wikibooks.org/wiki/Super_NES_Programming/SNES_memory_map
-- https://patpend.net/technical/snes/sneskart.html
local hardware_type = {
[0x00] = "ROM Only",
[0x01] = "ROM and RAM",
[0x02] = "ROM and Save RAM",
[0x03] = "ROM and DSP1"
}
-- Upperbound for ROM size, actual program size may be smaller.
local rom_ubound = {
[0x08] = "2 megabits",
[0x09] = "4 megabits",
[0x0A] = "8 megabits",
[0x0B] = "16 megabits",
[0x0C] = "32 megabits",
[0x0D] = "64 megabits",
}
-- Translates size in header to KBytes.
local rom_size_kb_tbl = {
[0x08] = 2 * 128,
[0x09] = 4 * 128,
[0x0A] = 8 * 128,
[0x0B] = 16 * 128,
[0x0C] = 32 * 128,
[0x0D] = 64 * 128,
}
local ram_size = {
[0x00] = "No sram",
[0x01] = "16 kilobits",
[0x02] = "32 kilobits",
[0x03] = "64 kilobits",
}
local destination_code = {
[0] = "Japan (NTSC)",
[1] = "USA (NTSC)",
[2] = "Australia, Europe, Oceania and Asia (PAL)",
[3] = "Sweden (PAL)",
[4] = "Finland (PAL)",
[5] = "Denmark (PAL)",
[6] = "France (PAL)",
[7] = "Holland (PAL)",
[8] = "Spain (PAL)",
[9] = "Germany, Austria and Switzerland (PAL)",
[10] = "Italy (PAL)",
[11] = "Hong Kong and China (PAL)",
[12] = "Indonesia (PAL)",
[13] = "Korea (PAL)",
}
local developer_code = {
[0x01] = 'Nintendo',
[0x03] = 'Imagineer-Zoom',
[0x05] = 'Zamuse',
[0x06] = 'Falcom',
[0x08] = 'Capcom',
[0x09] = 'HOT-B',
[0x0a] = 'Jaleco',
[0x0b] = 'Coconuts',
[0x0c] = 'Rage Software',
[0x0e] = 'Technos',
[0x0f] = 'Mebio Software',
[0x12] = 'Gremlin Graphics',
[0x13] = 'Electronic Arts',
[0x15] = 'COBRA Team',
[0x16] = 'Human/Field',
[0x17] = 'KOEI',
[0x18] = 'Hudson Soft',
[0x1a] = 'Yanoman',
[0x1c] = 'Tecmo',
[0x1e] = 'Open System',
[0x1f] = 'Virgin Games',
[0x20] = 'KSS',
[0x21] = 'Sunsoft',
[0x22] = 'POW',
[0x23] = 'Micro World',
[0x26] = 'Enix',
[0x27] = 'Loriciel/Electro Brain',
[0x28] = 'Kemco',
[0x29] = 'Seta Co.,Ltd.',
[0x2d] = 'Visit Co.,Ltd.',
[0x31] = 'Carrozzeria',
[0x32] = 'Dynamic',
[0x33] = 'Nintendo',
[0x34] = 'Magifact',
[0x35] = 'Hect',
[0x3c] = 'Empire Software',
[0x3d] = 'Loriciel',
[0x40] = 'Seika Corp.',
[0x41] = 'UBI Soft',
[0x46] = 'System 3',
[0x47] = 'Spectrum Holobyte',
[0x49] = 'Irem',
[0x4b] = 'Raya Systems/Sculptured Software',
[0x4c] = 'Renovation Products',
[0x4d] = 'Malibu Games/Black Pearl',
[0x4f] = 'U.S. Gold',
[0x50] = 'Absolute Entertainment',
[0x51] = 'Acclaim',
[0x52] = 'Activision',
[0x53] = 'American Sammy',
[0x54] = 'GameTek',
[0x55] = 'Hi Tech Expressions',
[0x56] = 'LJN Toys',
[0x5a] = 'Mindscape',
[0x5d] = 'Tradewest',
[0x5f] = 'American Softworks Corp.',
[0x60] = 'Titus',
[0x61] = 'Virgin Interactive Entertainment',
[0x62] = 'Maxis',
[0x67] = 'Ocean',
[0x69] = 'Electronic Arts',
[0x6b] = 'Laser Beam',
[0x6e] = 'Elite',
[0x6f] = 'Electro Brain',
[0x70] = 'Infogrames',
[0x71] = 'Interplay',
[0x72] = 'LucasArts',
[0x73] = 'Parker Brothers',
[0x75] = 'STORM',
[0x78] = 'THQ Software',
[0x79] = 'Accolade Inc.',
[0x7a] = 'Triffix Entertainment',
[0x7c] = 'Microprose',
[0x7f] = 'Kemco',
[0x80] = 'Misawa',
[0x81] = 'Teichio',
[0x82] = 'Namco Ltd.',
[0x83] = 'Lozc',
[0x84] = 'Koei',
[0x86] = 'Tokuma Shoten Intermedia',
[0x88] = 'DATAM-Polystar',
[0x8b] = 'Bullet-Proof Software',
[0x8c] = 'Vic Tokai',
[0x8e] = 'Character Soft',
[0x8f] = 'I\'\'Max',
[0x90] = 'Takara',
[0x91] = 'CHUN Soft',
[0x92] = 'Video System Co., Ltd.',
[0x93] = 'BEC',
[0x95] = 'Varie',
[0x97] = 'Kaneco',
[0x99] = 'Pack in Video',
[0x9a] = 'Nichibutsu',
[0x9b] = 'TECMO',
[0x9c] = 'Imagineer Co.',
[0xa0] = 'Telenet',
[0xa4] = 'Konami',
[0xa5] = 'K.Amusement Leasing Co.',
[0xa7] = 'Takara',
[0xa9] = 'Technos Jap.',
[0xaa] = 'JVC',
[0xac] = 'Toei Animation',
[0xad] = 'Toho',
[0xaf] = 'Namco Ltd.',
[0xb1] = 'ASCII Co. Activison',
[0xb2] = 'BanDai America',
[0xb4] = 'Enix',
[0xb6] = 'Halken',
[0xba] = 'Culture Brain',
[0xbb] = 'Sunsoft',
[0xbc] = 'Toshiba EMI',
[0xbd] = 'Sony Imagesoft',
[0xbf] = 'Sammy',
[0xc0] = 'Taito',
[0xc2] = 'Kemco',
[0xc3] = 'Square',
[0xc4] = 'Tokuma Soft',
[0xc5] = 'Data East',
[0xc6] = 'Tonkin House',
[0xc8] = 'KOEI',
[0xca] = 'Konami USA',
[0xcb] = 'NTVIC',
[0xcd] = 'Meldac',
[0xce] = 'Pony Canyon',
[0xcf] = 'Sotsu Agency/Sunrise',
[0xd0] = 'Disco/Taito',
[0xd1] = 'Sofel',
[0xd2] = 'Quest Corp.',
[0xd3] = 'Sigma',
[0xd6] = 'Naxat',
[0xd8] = 'Capcom Co., Ltd.',
[0xd9] = 'Banpresto',
[0xda] = 'Tomy',
[0xdb] = 'Acclaim',
[0xdd] = 'NCS',
[0xde] = 'Human Entertainment',
[0xdf] = 'Altron',
[0xe0] = 'Jaleco',
[0xe2] = 'Yutaka',
[0xe4] = 'T&ESoft',
[0xe5] = 'EPOCH Co.,Ltd.',
[0xe7] = 'Athena',
[0xe8] = 'Asmik',
[0xe9] = 'Natsume',
[0xea] = 'King Records',
[0xeb] = 'Atlus',
[0xec] = 'Sony Music Entertainment',
[0xee] = 'IGS',
[0xf1] = 'Motown Software',
[0xf2] = 'Left Field Entertainment',
[0xf3] = 'Beam Software',
[0xf4] = 'Tec Magik',
[0xf9] = 'Cybersoft',
[0xff] = 'Hudson Soft',
}
function hexfmt(val)
return string.format("0x%04X", val)
end
-- Function that determines if cartridge is lorom or hirom.
function detect_mapping()
local mapping = "unknown"
local map_mode_addr = 0xFFD5
local addr_rom_type = 0xFFD6
local addr_rom_size = 0xFFD7
local addr_sram_size = 0xFFD8
local addr_destination_code = 0xFFD9
dict.snes("SNES_SET_BANK", 0x00)
local maybe_map_mode = dict.snes("SNES_ROM_RD", map_mode_addr)
local valid_hirom_rom_type = hardware_type[dict.snes("SNES_ROM_RD", addr_rom_type)]
local valid_hirom_rom_size = rom_ubound[dict.snes("SNES_ROM_RD", addr_rom_size)]
local valid_hirom_sram_size = ram_size[dict.snes("SNES_ROM_RD", addr_sram_size)]
local valid_hirom_destination_code = destination_code[dict.snes("SNES_ROM_RD", addr_destination_code)]
local maybe_hirom = (maybe_map_mode & 1)
if (valid_hirom_rom_type and valid_hirom_rom_size and valid_hirom_sram_size and
valid_hirom_destination_code and maybe_hirom) then
mapping = hirom_name
else
mapping = lorom_name
end
return mapping
end
function seq_read(base_addr, n)
local rv = {}
local count = 0
while (count < n) do
local val = dict.snes("SNES_ROM_RD", base_addr + count)
count = count + 1
-- Kind of an ordering hack because Lua likes 1-based structures.
rv[count] = val
end
return rv
end
function string_from_bytes(base_addr, length)
local byte_table = seq_read(base_addr, length)
local s = ""
local count = 0
while (count < length) do
s = s .. string.char(byte_table[count + 1])
count = count + 1
end
return s
end
function word_from_two_bytes(base_addr)
local upper = dict.snes("SNES_ROM_RD", base_addr) << 8
local lower = dict.snes("SNES_ROM_RD", base_addr + 1)
return upper | lower
end
function print_header(internal_header)
local map_mode_str = lorom_name
if (internal_header["map_mode"] & 1) == 1 then map_mode_str = hirom_name end
print("Rom Title:\t\t" .. internal_header["rom_name"])
print("Map Mode:\t\t" .. map_mode_str)
print("Hardware Type:\t\t" .. hardware_type[internal_header["rom_type"]])
print("Rom Size Upper Bound:\t" .. rom_ubound[internal_header["rom_size"]])
print("SRAM Size:\t\t" .. ram_size[internal_header["sram_size"]])
print("Destination Code:\t" .. destination_code[internal_header["destination_code"]])
print("Developer:\t\t" .. developer_code[internal_header["developer_code"]])
print("Version:\t\t" .. hexfmt(internal_header["version"]))
print("Checksum:\t\t" .. hexfmt(internal_header["checksum"]))
end
function get_header()
local map_adjust = 0 -- Might be set to 0x8000 depending on mapping.
local mapping = detect_mapping()
if mapping == lorom_name then map_adjust = 0x8000 end
-- Rom Registration Addresses (15 bytes)
local addr_maker_code = 0xFFB0 - map_adjust -- 2 bytes
local addr_game_code = 0xFFB2 - map_adjust -- 4 bytes
local addr_fixed_zero = 0xFFB6 - map_adjust -- 7 bytes
local addr_expansion_ram_size = 0xFFBD - map_adjust -- 1 byte
local addr_special_version_code = 0xFFBE - map_adjust -- 1 byte
-- ROM Specification Addresses (32 bytes)
local addr_rom_name = 0xFFC0 - map_adjust -- 21 bytes
local addr_map_mode = 0xFFD5 - map_adjust -- 1 byte
local addr_rom_type = 0xFFD6 - map_adjust -- 1 byte
local addr_rom_size = 0xFFD7 - map_adjust -- 1 byte
local addr_sram_size = 0xFFD8 - map_adjust -- 1 byte
local addr_destination_code = 0xFFD9 - map_adjust -- 1 byte
local addr_developer_code = 0xFFDA - map_adjust -- 1 byte (This is actually manufacturer ID)
local addr_version = 0xFFDB - map_adjust -- 1 byte
local addr_compliment_check = 0xFFDC - map_adjust -- 2 bytes
local addr_checksum = 0xFFDD - map_adjust -- 2 bytes
local internal_header = {
mapping = mapping,
rom_name = string_from_bytes(addr_rom_name, 21),
map_mode = dict.snes("SNES_ROM_RD", addr_map_mode),
rom_type = dict.snes("SNES_ROM_RD", addr_rom_type),
rom_size = dict.snes("SNES_ROM_RD", addr_rom_size),
sram_size = dict.snes("SNES_ROM_RD", addr_sram_size),
destination_code = dict.snes("SNES_ROM_RD", addr_destination_code),
developer_code = dict.snes("SNES_ROM_RD", addr_developer_code),
version = dict.snes("SNES_ROM_RD", addr_version),
compliment_check = word_from_two_bytes(addr_compliment_check),
checksum = word_from_two_bytes(addr_checksum)
}
return internal_header
end
function test()
local internal_header = get_header()
print_header(internal_header)
end
-- local functions
@ -420,7 +751,18 @@ local function process(process_opts, console_opts)
local rv = nil
local file
--initialize device i/o for SNES
dict.io("IO_RESET")
dict.io("SNES_INIT")
local internal_header = get_header()
local snes_mapping = console_opts["mapper"]
if snes_mapping == "" then
snes_mapping = lorom_name
if (internal_header["map_mode"] & 1) == 1 then map_mode_str = hirom_name end
print("Mapping not provided, " .. snes_mapping .. " detected.")
end
--local ram_size = 448 --max LOROM RAM size 32KByte * 0x70-0x7D banks
--local ram_size = 32 --just a single bank of LOROM RAM
@ -431,7 +773,11 @@ local function process(process_opts, console_opts)
local ramdumpfile = process_opts["dumpram_filename"]
local rom_size = console_opts["rom_size_kbyte"]
if rom_size == 0 then
rom_size = rom_size_kb_tbl[internal_header["rom_size"]]
print("ROM Size not provided, " .. rom_ubound[internal_header["rom_size"]] .. " detected.")
end
-- SNES memory map banking
-- A15 always high for LOROM (A22 is typically low too)
@ -457,16 +803,15 @@ local function process(process_opts, console_opts)
end
--initialize device i/o for SNES
dict.io("IO_RESET")
dict.io("SNES_INIT")
--test cart by reading manf/prod ID
if test then
print("Testing SNES board");
test()
--[[
--SNES detect HiROM or LoROM & RAM
--SNES detect if able to read flash ID's
@ -474,6 +819,7 @@ local function process(process_opts, console_opts)
print("ERROR unable to read flash ID")
return
end
--]]
end