773 lines
24 KiB
Lua
773 lines
24 KiB
Lua
|
|
-- create the module's table
|
|
local genesis_v2= {}
|
|
|
|
-- import required modules
|
|
local dict = require "scripts.app.dict"
|
|
local dump = require "scripts.app.dump"
|
|
local flash = require "scripts.app.flash"
|
|
local help = require "scripts.app.help"
|
|
|
|
-- file constants
|
|
|
|
-- local functions
|
|
|
|
local function unsupported(operation)
|
|
print("\nUNSUPPORTED OPERATION: \"" .. operation .. "\" not implemented yet for Sega Genesis.\n")
|
|
end
|
|
|
|
-- Compute Genesis checksum from a file, which can be compared with header value.
|
|
local function checksum_rom(filename)
|
|
local file = assert(io.open(filename, "rb"))
|
|
local sum = 0
|
|
-- Skip header
|
|
file:read(0x200)
|
|
while true do
|
|
-- Add up remaining 16-bit words
|
|
local bytes = file:read(2)
|
|
if not bytes then break end
|
|
sum = sum + string.unpack(">i2", bytes)
|
|
end
|
|
-- Only use the lower bits.
|
|
return sum & 0xFFFF
|
|
end
|
|
|
|
--/ROMSEL is always low for this dump
|
|
local function dump_rom( file, rom_size_KB, debug )
|
|
|
|
local KB_per_bank = 128 -- A1-16 = 64K address space, 2Bytes per address
|
|
local addr_base = 0x0000 -- control signals are manually controlled
|
|
|
|
|
|
local num_reads = rom_size_KB / KB_per_bank
|
|
local read_count = 0
|
|
|
|
while (read_count < num_reads) do
|
|
|
|
if debug then print( "Dumping ROM part ", read_count + 1, " of ", num_reads) end
|
|
|
|
-- A "large" Genesis ROM is 24 banks, many are 8 and 16 - status every 4 is reasonable.
|
|
-- The largest published Genesis game is Super Street Fighter 2, which is 40 banks!
|
|
-- TODO: Accessing banks in games that are >4MB require using a mapper.
|
|
-- See: https://plutiedev.com/beyond-4mb
|
|
|
|
if (read_count % 4 == 0) then
|
|
print("dumping ROM bank: ", read_count, " of ", num_reads - 1)
|
|
end
|
|
|
|
-- Select desired bank.
|
|
dict.sega("GEN_SET_BANK", read_count)
|
|
|
|
dump.dumptofile(file, KB_per_bank/2, addr_base, "GENESIS_ROM_PAGE0", debug)
|
|
dump.dumptofile(file, KB_per_bank/2, addr_base, "GENESIS_ROM_PAGE1", debug)
|
|
|
|
read_count = read_count + 1
|
|
end
|
|
|
|
end
|
|
|
|
-- Helper to extract fields in internal header.
|
|
local function extract_field_from_string(data, start_offset, length)
|
|
-- 1 is added to Offset to handle lua strings being 1-based.
|
|
return string.sub(data, start_offset + 1, start_offset + length)
|
|
end
|
|
|
|
-- Populates table with internal header contents from dumped data.
|
|
local function extract_header(header_data)
|
|
-- https://plutiedev.com/rom-header
|
|
-- https://en.wikibooks.org/wiki/Genesis_Programming#ROM_header
|
|
|
|
-- TODO: Decode publisher from t-series in build field
|
|
-- https://segaretro.org/Third-party_T-series_codes
|
|
|
|
local addr_console_name = 0x100
|
|
local addr_build_date = 0x110
|
|
local addr_domestic_name = 0x120
|
|
local addr_intl_name = 0x150
|
|
local addr_type_serial_version = 0x180
|
|
local addr_checksum = 0x18E
|
|
local addr_device_support = 0x190
|
|
local addr_rom_addr_range = 0x1A0
|
|
local addr_ram_addr_range = 0x1A8
|
|
local addr_sram_support = 0x1B0
|
|
local addr_modem_support = 0x1BC
|
|
local addr_region_support = 0x1F0
|
|
|
|
local len_console_name = 16
|
|
local len_build_date = 16
|
|
local len_name = 48
|
|
local len_type_serial_version = 14
|
|
local len_checksum = 2
|
|
local len_device_support = 16
|
|
local len_addr_range = 8
|
|
local len_sram_support = 12
|
|
local len_modem_support = 12
|
|
local len_region_support = 3
|
|
|
|
local header = {
|
|
console_name = extract_field_from_string(header_data, addr_console_name, len_console_name),
|
|
-- TODO: Decode T-Value and build info.
|
|
build_date = extract_field_from_string(header_data, addr_build_date, len_build_date),
|
|
domestic_name = extract_field_from_string(header_data, addr_domestic_name, len_name),
|
|
international_name = extract_field_from_string(header_data, addr_intl_name, len_name),
|
|
-- TODO: Decode Type, serial and revision.
|
|
type_serial_version = extract_field_from_string(header_data, addr_type_serial_version, len_type_serial_version),
|
|
checksum = string.unpack(">i2", extract_field_from_string(header_data, addr_checksum, len_checksum)),
|
|
-- TODO: Decode device support.
|
|
io_device_support = extract_field_from_string(header_data, addr_device_support, len_device_support),
|
|
-- TODO: Decode SRAM support.
|
|
sram_support = extract_field_from_string(header_data, addr_sram_support, len_sram_support),
|
|
-- TODO: Decode modem support.
|
|
modem_support = extract_field_from_string(header_data, addr_modem_support, len_modem_support),
|
|
-- TODO: Decode region support.
|
|
region_support = extract_field_from_string(header_data, addr_region_support, len_region_support),
|
|
}
|
|
-- ROM range can be used to autodetect the rom size.
|
|
local rom_range = extract_field_from_string(header_data, addr_rom_addr_range, len_addr_range)
|
|
local rom_start = string.unpack(">i4", string.sub(rom_range, 1, 4))
|
|
local rom_end = string.unpack(">i4", string.sub(rom_range,5, 8))
|
|
header["rom_size"] = (rom_end - rom_start + 1) / 1024
|
|
|
|
-- These should be the same in every cart according to docs, but decode in case its not. (64 Kb)
|
|
local ram_range = extract_field_from_string(header_data, addr_ram_addr_range, len_addr_range)
|
|
local ram_start = string.unpack(">i4", string.sub(ram_range, 1, 4))
|
|
local ram_end = string.unpack(">i4", string.sub(ram_range,5, 8))
|
|
header["ram_size"] = (ram_end - ram_start + 1) / 1024
|
|
|
|
return header
|
|
end
|
|
|
|
-- Make a human-friendly text representation of ROM Size.
|
|
local function str_rom_size(rom_size_kb)
|
|
local mbit = rom_size_kb / 128
|
|
if mbit < 1 then
|
|
mbit = "<1"
|
|
end
|
|
return "" .. rom_size_kb .. " kB (".. mbit .." mbit)"
|
|
end
|
|
|
|
-- Prints parsed header contents to stdout.
|
|
local function print_header(genesis_header)
|
|
print("Console Name: \t" .. genesis_header["console_name"])
|
|
print("Domestic Name: \t" .. genesis_header["domestic_name"])
|
|
print("Release Date: \t" .. genesis_header["build_date"])
|
|
print("Rom Size: \t" .. str_rom_size(genesis_header["rom_size"]))
|
|
print("Serial/Version: " .. genesis_header["type_serial_version"])
|
|
print("Checksum: \t" .. hexfmt(genesis_header["checksum"]))
|
|
end
|
|
|
|
-- Reads and parses internal ROM header from first page of data.
|
|
local function read_header()
|
|
dict.sega("GEN_SET_BANK", 0)
|
|
|
|
local page0_data = ""
|
|
dump.dumptocallback(
|
|
function (data)
|
|
page0_data = page0_data .. data
|
|
end,
|
|
64, 0x0000, "GENESIS_ROM_PAGE0", false
|
|
)
|
|
local header_data = string.sub(page0_data, 1, 0x201)
|
|
local genesis_header = extract_header(header_data)
|
|
return genesis_header
|
|
end
|
|
|
|
-- Test that cartridge is readable by looking for valid entries in internal header.
|
|
local function test(genesis_header)
|
|
|
|
---[[
|
|
--test some functions
|
|
--read "SEGA" from the in rom header
|
|
dict.sega("GEN_SET_BANK", 0)
|
|
local temp
|
|
temp = dict.sega("GEN_ROM_RD", (0x0100>>1))
|
|
print(help.hex(temp)) --"SE"
|
|
print(string.char(temp>>8)) --"S"
|
|
print(string.char(temp&0x00FF)) --"E"
|
|
temp = dict.sega("GEN_ROM_RD", (0x0102>>1))
|
|
print(help.hex(temp)) --"GA"
|
|
print(string.char(temp>>8)) --"G"
|
|
print(string.char(temp&0x00FF)) --"A"
|
|
|
|
--flash manf ID
|
|
print("flash write")
|
|
dict.sega("GEN_SET_BANK", 0)
|
|
dict.sega("GEN_SET_ADDR", 0x5555)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x00AA, 0)
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x2AAA)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x0055, 0)
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x5555)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x0090, 0)
|
|
temp = dict.sega("GEN_ROM_RD", (0x0000>>1))
|
|
print(help.hex(temp)) --"SE"
|
|
temp = dict.sega("GEN_ROM_RD", (0x0002>>1))
|
|
print(help.hex(temp)) --"SE"
|
|
|
|
print("HI write")
|
|
dict.sega("GEN_WR_HI", 0x5555, 0xAA) --A16-1
|
|
dict.sega("GEN_WR_HI", 0x2AAA, 0x55) --A16-1
|
|
dict.sega("GEN_WR_HI", 0x5555, 0x90) --A16-1
|
|
temp = dict.sega("GEN_ROM_RD", (0x0000>>1))
|
|
print(help.hex(temp)) --"SE"
|
|
temp = dict.sega("GEN_ROM_RD", (0x0002>>1))
|
|
print(help.hex(temp)) --"SE"
|
|
|
|
--exit software mode
|
|
print("exit software mode")
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x00F0, 0)
|
|
temp = dict.sega("GEN_ROM_RD", (0x0000>>1))
|
|
print(help.hex(temp)) --"SE"
|
|
|
|
|
|
--write a byte
|
|
--[[
|
|
print("write a byte $0000, AAAA")
|
|
dict.sega("GEN_SET_BANK", 0)
|
|
dict.sega("GEN_SET_ADDR", 0x5555)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x00AA, 0)
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x2AAA)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x0055, 0)
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x5555)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x00A0, 0) --write byte command
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x0000)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0xAAAA, 0) --write data
|
|
|
|
temp = dict.sega("GEN_ROM_RD", (0x0000>>1))
|
|
local nak = 1
|
|
while (temp ~= dict.sega("GEN_ROM_RD", (0x0000>>1))) do
|
|
temp = dict.sega("GEN_ROM_RD", (0x0000>>1))
|
|
print(help.hex(temp)) --"SE"
|
|
nak = nak + 1
|
|
end
|
|
temp = dict.sega("GEN_ROM_RD", (0x0000>>1))
|
|
print("FINAL DATA", help.hex(temp)) --"SE"
|
|
|
|
local addr = 0x0001
|
|
local data = 0x5555
|
|
print("write a byte", addr, data )
|
|
dict.sega("GEN_SET_BANK", 0)
|
|
dict.sega("GEN_SET_ADDR", 0x5555)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x00AA, 0)
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x2AAA)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x0055, 0)
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x5555)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x00A0, 0) --write byte command
|
|
|
|
dict.sega("GEN_SET_ADDR", addr)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", data, 0) --write data
|
|
|
|
temp = dict.sega("GEN_ROM_RD", (addr))
|
|
local nak = 1
|
|
while (temp ~= dict.sega("GEN_ROM_RD", (addr))) do
|
|
temp = dict.sega("GEN_ROM_RD", (addr))
|
|
print(help.hex(temp)) --"SE"
|
|
nak = nak + 1
|
|
end
|
|
temp = dict.sega("GEN_ROM_RD", (addr))
|
|
print("FINAL DATA", help.hex(temp)) --"SE"
|
|
--]]
|
|
|
|
|
|
--[[
|
|
--read ram from wayne gretzky
|
|
dict.sega("GEN_SET_BANK", 0x20>>1)
|
|
temp = dict.sega("GEN_ROM_RD", (0x0000>>1))
|
|
print(help.hex(temp)) --"SE"
|
|
--print(string.char(temp>>8)) --"S"
|
|
print(string.char(temp&0x00FF)) --"E"
|
|
--temp = dict.sega("GEN_ROM_RD", (0x0002>>1))
|
|
--print(help.hex(temp)) --"GA"
|
|
----print(string.char(temp>>8)) --"G"
|
|
--print(string.char(temp&0x00FF)) --"A"
|
|
|
|
--dict.sega("GEN_SET_ADDR", 0x0000) --A16-1
|
|
dict.sega("GEN_WR_LO", 0x0000, 0xAA) --A16-1
|
|
temp = dict.sega("GEN_ROM_RD", (0x0000>>1))
|
|
print(help.hex(temp)) --"SE"
|
|
print(string.char(temp&0x00FF)) --"E"
|
|
--]]
|
|
|
|
|
|
--local valid = false
|
|
local valid = true --force good
|
|
-- Trailing spaces are required! Field length is 16 characters.
|
|
if genesis_header["console_name"] == "SEGA GENESIS " then valid = true end
|
|
if genesis_header["console_name"] == "SEGA MEGA DRIVE " then valid = true end
|
|
return valid
|
|
end
|
|
|
|
|
|
--dump the SEGA battery RAM starting at the provided bank
|
|
local function dump_ram( file, start_bank, ram_size_KB, debug )
|
|
|
|
local KB_per_bank = 64 --128KByte addressable per bank, but only use lower byte of each 16bit word
|
|
local addr_base = 0x00 --A15-8 address of ram start
|
|
|
|
-- --determine max ram per bank and base address
|
|
-- if (mapping == lorom_name) then
|
|
-- KB_per_bank = 32 -- LOROM has 32KB per bank
|
|
-- addr_base = 0x00 -- $0000 LOROM RAM start address
|
|
-- elseif (mapping == hirom_name) then
|
|
-- KB_per_bank = 8 -- HIROM has 8KB per bank
|
|
-- addr_base = 0x60 -- $6000 HIROM RAM start address
|
|
-- else
|
|
-- print("ERROR! mapping:", mapping, "not supported by dump_ram")
|
|
-- end
|
|
--
|
|
local num_banks =1-- = ram_size_KB / KB_per_bank
|
|
--
|
|
-- --determine how much ram to read per bank
|
|
-- if ram_size_KB == nil then ram_size_KB = 0 end
|
|
-- if (ram_size_KB < KB_per_bank) then
|
|
-- num_banks = 1
|
|
-- KB_per_bank = ram_size_KB
|
|
-- else
|
|
-- num_banks = ram_size_KB / KB_per_bank
|
|
-- end
|
|
--
|
|
local read_count = 0
|
|
|
|
while ( read_count < num_banks ) do
|
|
|
|
if debug then print( "dump RAM part ", read_count, " of ", num_banks) end
|
|
|
|
--select desired bank
|
|
--A17-23
|
|
dict.sega("GEN_SET_BANK", start_bank+read_count)
|
|
|
|
-- if (mapping == lorom_name) then --LOROM sram is inside /ROMSEL space
|
|
-- dump.dumptofile( file, KB_per_bank, addr_base, "SNESROM_PAGE", false )
|
|
-- else -- HIROM is outside of /ROMSEL space
|
|
-- dump.dumptofile( file, KB_per_bank, addr_base, "SNESSYS_PAGE", false )
|
|
-- end
|
|
--
|
|
--currently don't have means of dumping RAM with A16 high
|
|
--dump.dumptofile(file, KB_per_bank/2, addr_base, "GENESIS_RAM_PAGE", debug) --A16 low
|
|
dump.dumptofile(file, 8, addr_base, "GENESIS_RAM_PAGE", debug) --A16 low
|
|
-- dump.dumptofile(file, KB_per_bank/2, addr_base, "GENESIS_ROM_PAGE1", debug) --A16 high
|
|
|
|
read_count = read_count + 1
|
|
end
|
|
|
|
end
|
|
|
|
--write to the WRAM, assumes the WRAM was enabled/disabled as desired prior to calling
|
|
local function write_ram(file, ram_size_KB, debug)
|
|
|
|
-- init_mapper()
|
|
|
|
--test some bytes
|
|
--wr_prg_flash_byte(0x0000, 0xA5, true)
|
|
--wr_prg_flash_byte(0x0FFF, 0x5A, true)
|
|
|
|
print("\nProgramming battery SRAM")
|
|
--initial testing of MMC3 with no specific MMC3 flash firmware functions 6min per 256KByte = 0.7KBps
|
|
|
|
|
|
local base_addr = 0x0000 --writes occur $6000-7FFF
|
|
local bank_size = 8*1024 --8KByte RAM chip
|
|
local buff_size = 1 --number of bytes to write at a time
|
|
local cur_bank = 0
|
|
-- local total_banks = ram_size_KB*1024/bank_size
|
|
|
|
local byte_num --byte number gets reset for each bank
|
|
local byte_str, data, readdata
|
|
local rv
|
|
local timout
|
|
|
|
|
|
--while cur_bank < total_banks do
|
|
|
|
-- if (cur_bank %8 == 0) then
|
|
-- print("writting RAM bank: ", cur_bank, " of ", total_banks-1)
|
|
-- end
|
|
|
|
--write the current bank to the mapper register
|
|
--DATA writes written to $6000-7FFF
|
|
-- dict.nes("NES_CPU_WR", 0x5113, cur_bank) --PRG-RAM bank @ $6000-7FFF (regardless of PRG mode)
|
|
dict.sega("GEN_SET_BANK", (0x20>>1))
|
|
|
|
|
|
--program the entire bank's worth of data
|
|
|
|
---[[ This version of the code programs a single byte at a time but doesn't require
|
|
-- MMC3 specific functions in the firmware
|
|
--print("This is slow as molasses, but gets the job done")
|
|
byte_num = 0 --current byte within the bank
|
|
while byte_num < bank_size do
|
|
|
|
--read next byte from the file and convert to binary
|
|
byte_str = file:read(buff_size)
|
|
data = string.unpack("B", byte_str, 1)
|
|
|
|
--write the data
|
|
--SLOWEST OPTION: no firmware MMC3 specific functions 100% host flash algo:
|
|
--wr_prg_flash_byte(base_addr+byte_num, data, false) --0.7KBps
|
|
|
|
--need to quickly write the byte after unlocking the PRG-RAM
|
|
--before the 11.2usec timeout happens
|
|
rv = dict.sega("GEN_WR_LO", base_addr+byte_num, data)
|
|
|
|
--if (rv == data) then
|
|
-- --write succeeded
|
|
-- timeout = 0
|
|
--else
|
|
-- print("PRG-RAM byte failed to write, retrying")
|
|
-- rv = dict.nes("MMC5_PRG_RAM_WR", base_addr+byte_num, data) --3.8KBps (5.5x faster than above)
|
|
-- if (rv ~= data) then
|
|
-- print("FAILED on RETRY...")
|
|
-- end
|
|
--end
|
|
|
|
byte_num = byte_num + 1
|
|
end
|
|
--]]
|
|
|
|
--Have the device write a banks worth of data
|
|
--FAST! 13sec for 512KB = 39KBps
|
|
--flash.write_file( file, bank_size/1024, mapname, "PRGROM", false )
|
|
--flash.write_file( file, bank_size/1024, "NOVAR", "PRGRAM", false )
|
|
|
|
-- cur_bank = cur_bank + 1
|
|
-- end
|
|
|
|
print("Done Programming SAVE RAM")
|
|
|
|
end
|
|
|
|
--write a single byte to SNES ROM flash
|
|
--writes to currently selected bank address
|
|
local function wr_flash_byte(addr, value, debug)
|
|
|
|
if (addr < 0x0000 or addr > 0xFFFF) then
|
|
print("\n ERROR! flash write to SEGA GENESIS", string.format("$%X", addr), "must be $0000-FFFF \n\n")
|
|
return
|
|
end
|
|
|
|
--send unlock command and write byte
|
|
--dict.snes("SNES_ROM_WR", 0x8AAA, 0xAA)
|
|
--dict.snes("SNES_ROM_WR", 0x8555, 0x55)
|
|
--dict.snes("SNES_ROM_WR", 0x8AAA, 0xA0)
|
|
--dict.snes("SNES_ROM_WR", addr, value)
|
|
|
|
if debug then print("write a byte", help.hex(addr), help.hex(value) ) end
|
|
--dict.sega("GEN_SET_BANK", 0)
|
|
dict.sega("GEN_SET_ADDR", 0x5555)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x00AA, 0)
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x2AAA)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x0055, 0)
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x5555)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x00A0, 0) --write byte command
|
|
|
|
dict.sega("GEN_SET_ADDR", addr)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", value, 0) --write value
|
|
|
|
-- temp = dict.sega("GEN_ROM_RD", (addr))
|
|
-- local nak = 1
|
|
-- while (temp ~= dict.sega("GEN_ROM_RD", (addr))) do
|
|
-- temp = dict.sega("GEN_ROM_RD", (addr))
|
|
-- print(help.hex(temp)) --"SE"
|
|
-- nak = nak + 1
|
|
-- end
|
|
-- temp = dict.sega("GEN_ROM_RD", (addr))
|
|
-- print("FINAL DATA", help.hex(temp)) --"SE"
|
|
|
|
local rv = dict.sega("GEN_ROM_RD", (addr))
|
|
|
|
local i = 0
|
|
|
|
while ( rv ~= value ) do
|
|
rv = dict.sega("GEN_ROM_RD", (addr))
|
|
--if debug then print("post write read:", help.hex(rv)) end
|
|
i = i + 1
|
|
if i > 20 then
|
|
print("failed write, tried:", string.format("%X",value), "read back value:", string.format("%X",rv))
|
|
return
|
|
end
|
|
end
|
|
if debug then print(i, "naks, done writing byte.") end
|
|
if debug then print("written value:", string.format("%X",value), "verified value:", string.format("%X",rv)) end
|
|
|
|
--TODO handle timeout for problems
|
|
|
|
--TODO return pass/fail/info
|
|
end
|
|
|
|
|
|
local function flash_rom(file, rom_size_KB, debug)
|
|
|
|
print("\nProgramming ROM flash")
|
|
|
|
--test some bytes
|
|
-- dict.sega("GEN_SET_BANK", 0x00) wr_flash_byte(0x0000, 0xAAAA, true) wr_flash_byte(0x0001, 0x5555, true)
|
|
-- dict.sega("GEN_SET_BANK", 0x00) wr_flash_byte(0x0002, 0x0000, true) wr_flash_byte(0x0003, 0xC3C3, true)
|
|
-- dict.sega("GEN_SET_BANK", 0x00) wr_flash_byte(0x0004, 0xDEAD, true) wr_flash_byte(0x0005, 0xBEEF, true)
|
|
-- dict.sega("GEN_SET_BANK", 0x00) wr_flash_byte(0x0006, 0x3333, true) wr_flash_byte(0x0007, 0xCCCC, true)
|
|
-- --last of 512KB
|
|
-- if true then return end
|
|
|
|
--most of this is overkill for NROM, but it's how we want to handle things for bigger mappers
|
|
local base_addr = 0x0000
|
|
local bank_size = 2*64*1024 --2Bytes per address, 64K addresses
|
|
local buff_size = 1 --number of bytes to read from file at a time
|
|
local cur_bank = 0
|
|
|
|
-- if (mapping==lorom_name) then
|
|
-- base_addr = 0x8000 --writes occur $8000-FFFF
|
|
-- bank_size = 32*1024 --SNES LOROM 32KB per ROM bank
|
|
-- elseif (mapping==hirom_name) then
|
|
-- base_addr = 0x0000 --writes occur $0000-FFFF
|
|
-- bank_size = 64*1024 --SNES HIROM 64KB per ROM bank
|
|
-- else
|
|
-- print("ERROR!! mapping:", mapping, "not supported")
|
|
-- end
|
|
|
|
local total_banks = rom_size_KB*1024/bank_size
|
|
|
|
local byte_num --byte number gets reset for each bank
|
|
local byte_str, data, readdata
|
|
|
|
while cur_bank < total_banks do
|
|
|
|
if (cur_bank %4 == 0) then
|
|
print("writting ROM bank: ", cur_bank, " of ", total_banks-1)
|
|
end
|
|
|
|
--select the current bank
|
|
if (cur_bank <= 0x7F) then
|
|
--dict.sega("GEN_SET_BANK", (cur_bank>>1)) --genesis bank is off by 1 due to lack of A0
|
|
dict.sega("GEN_SET_BANK", (cur_bank)) --Don't think that's acutally true this bank is true INLretro bank
|
|
else
|
|
print("\n\nERROR!!!! SEGA bank cannot exceed 0x7F, it was:", string.format("0x%X",cur_bank))
|
|
return
|
|
end
|
|
|
|
|
|
--program the entire bank's worth of data
|
|
|
|
---[[ This version of the code programs a single byte at a time but doesn't require
|
|
-- board specific functions in the firmware
|
|
print("This is slow as molasses, but gets the job done")
|
|
|
|
--SET ADDR so FLASH_WR_ADDROFF works
|
|
dict.sega("GEN_SET_ADDR", 0xFFFF)
|
|
|
|
byte_num = 0 --current byte within the bank
|
|
while byte_num < bank_size do
|
|
|
|
--read next byte from the file and convert to binary
|
|
byte_str = file:read(buff_size) --high byte
|
|
data = string.unpack("B", byte_str, 1)
|
|
--print(help.hex(data))
|
|
data = data<<8
|
|
--print(help.hex(data))
|
|
byte_str = file:read(buff_size) --low byte
|
|
data = data + string.unpack("B", byte_str, 1)
|
|
--print(help.hex(data))
|
|
|
|
--write the data
|
|
--SLOWEST OPTION: no firmware specific functions 100% host flash algo:
|
|
--wr_flash_byte(((base_addr+byte_num)>>1), data, false) --0.7KBps
|
|
--EASIEST FIRMWARE SPEEDUP: 5x faster, create firmware write byte function:
|
|
dict.sega("GEN_SST_FLASH_WR_ADDROFF", data, 1)
|
|
|
|
--if (verify) then
|
|
-- readdata = dict.nes("NES_CPU_RD", base_addr+byte_num)
|
|
-- if readdata ~= data then
|
|
-- print("ERROR flashing byte number", byte_num, " in bank",cur_bank, " to flash ", data, readdata)
|
|
-- end
|
|
--end
|
|
|
|
byte_num = byte_num + 2 --2 Bytes per write
|
|
end
|
|
--]]
|
|
|
|
--Have the device write a banks worth of data
|
|
-- if (mapping == lorom_name) then
|
|
-- flash.write_file( file, bank_size/1024, "LOROM_3VOLT", "SNESROM", false )
|
|
-- else
|
|
-- flash.write_file( file, bank_size/1024, "HIROM_3VOLT", "SNESROM", false )
|
|
-- end
|
|
|
|
--flash.write_file( file, bank_size/1024, "HIROM_3VOLT", "GENESISROM", false )
|
|
--TODO define different flash part types
|
|
--flash.write_file( file, bank_size/1024, 0, "GENESISROM", false )
|
|
|
|
cur_bank = cur_bank + 1
|
|
end
|
|
|
|
print("Done Programming ROM flash")
|
|
|
|
end
|
|
|
|
--Cart should be in reset state upon calling this function
|
|
--this function processes all user requests for this specific board/mapper
|
|
local function process(process_opts, console_opts)
|
|
local file
|
|
|
|
-- Use specified ram size if provided, otherwise autodetect.
|
|
local ram_size = console_opts["wram_size_kb"]
|
|
local ramdumpfile = process_opts["dumpram_filename"]
|
|
--local flashfile = process_opts["flash_filename"]
|
|
local flashfile = process_opts["flash_filename"]
|
|
local verifyfile = process_opts["verify_filename"]
|
|
local rom_size = console_opts["rom_size_kbyte"]
|
|
|
|
-- Initialize device i/o for SEGA
|
|
dict.io("IO_RESET")
|
|
dict.io("SEGA_INIT")
|
|
local genesis_header = read_header()
|
|
|
|
if process_opts["test"] then
|
|
-- If garbage data is in the header, it's a waste of time trying to proceed doing anything else.
|
|
local valid_header = test(genesis_header)
|
|
if valid_header ~= true then print("Unreadable cartridge - exiting! (Try cleaning cartridge connector?)") end
|
|
assert(valid_header)
|
|
print_header(genesis_header)
|
|
end
|
|
|
|
-- TODO: dump the ram to file
|
|
if process_opts["dumpram"] then
|
|
--unsupported("dumpram")
|
|
print("dumping save RAM")
|
|
|
|
|
|
file = assert(io.open(ramdumpfile, "wb"))
|
|
|
|
--dump cart into file
|
|
local rambank = (0x20>>1) --A17-23 wayne gretsky RAM starts at bank $20>>1
|
|
|
|
dump_ram(file, rambank, ram_size, true)
|
|
|
|
--may disable SRAM by placing /RESET low
|
|
|
|
--close file
|
|
assert(file:close())
|
|
|
|
print("DONE Dumping SAVE RAM")
|
|
end
|
|
|
|
-- Dump the cart to dumpfile.
|
|
if process_opts["read"] then
|
|
|
|
-- If ROM size wasn't provided, attempt to use value in internal header.
|
|
local rom_size = console_opts["rom_size_kbyte"]
|
|
if rom_size == 0 then
|
|
print("ROM Size not provided, " .. str_rom_size(genesis_header["rom_size"]) .. " detected.")
|
|
rom_size = genesis_header["rom_size"]
|
|
end
|
|
|
|
print("\nDumping SEGA ROM...")
|
|
file = assert(io.open(process_opts["dump_filename"], "wb"))
|
|
|
|
--dump cart into file
|
|
dump_rom(file, rom_size, false)
|
|
|
|
--close file
|
|
assert(file:close())
|
|
print("DONE Dumping SEGA ROM")
|
|
print("Computing checksum...")
|
|
local checksum = checksum_rom(process_opts["dump_filename"])
|
|
if checksum == genesis_header["checksum"] then
|
|
print("CHECKSUM OK! DUMP SUCCESS!")
|
|
else
|
|
print("CHECKSUM MISMATCH - BAD DUMP! (Try cleaning cartridge connector?)")
|
|
end
|
|
end
|
|
|
|
if process_opts["erase"] then
|
|
-- unsupported("erase")
|
|
--erase the cart
|
|
print("erasing SST flash cart")
|
|
dict.sega("GEN_SET_BANK", 0)
|
|
dict.sega("GEN_SET_ADDR", 0x5555)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x00AA, 0)
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x2AAA)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x0055, 0)
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x5555)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x0080, 0) --ERASE
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x5555)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x00AA, 0) --ERASE
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x2AAA)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x0055, 0) --ERASE
|
|
|
|
dict.sega("GEN_SET_ADDR", 0x5555)
|
|
dict.sega("GEN_FLASH_WR_ADDROFF", 0x0010, 0) --ERASE
|
|
|
|
|
|
temp = dict.sega("GEN_ROM_RD", (0))
|
|
local nak = 1
|
|
while (temp ~= dict.sega("GEN_ROM_RD", (0))) do
|
|
temp = dict.sega("GEN_ROM_RD", (0))
|
|
--print(help.hex(temp)) --"SE"
|
|
nak = nak + 1
|
|
end
|
|
temp = dict.sega("GEN_ROM_RD", (0))
|
|
print("DONE ERASING, FINAL DATA", help.hex(temp)) --"SE"
|
|
end
|
|
|
|
-- TODO: write to wram on the cart
|
|
--if writeram then
|
|
if process_opts["writeram"] then
|
|
--unsupported("writeram")
|
|
print("\nWritting to WRAM...")
|
|
|
|
file = assert(io.open(process_opts["writeram_filename"], "rb"))
|
|
--write_ram(file, ram_size_KB, debug)
|
|
write_ram(file, ram_size, true)
|
|
|
|
assert(file:close())
|
|
|
|
print("DONE Writting WRAM")
|
|
end
|
|
|
|
-- TODO: program flashfile to the cart
|
|
if process_opts["program"] then
|
|
--unsupported("program")
|
|
|
|
--open file
|
|
file = assert(io.open(flashfile, "rb"))
|
|
--determine if auto-doubling, deinterleaving, etc,
|
|
--needs done to make board compatible with rom
|
|
|
|
--flash cart
|
|
flash_rom(file, rom_size, true)
|
|
|
|
--close file
|
|
assert(file:close())
|
|
end
|
|
|
|
-- TODO: verify flashfile is on the cart
|
|
if process_opts["verify"] then
|
|
unsupported("verify")
|
|
end
|
|
|
|
dict.io("IO_RESET")
|
|
end
|
|
|
|
|
|
-- global variables so other modules can use them
|
|
-- NONE
|
|
|
|
-- call functions desired to run when script is called/imported
|
|
-- NONE
|
|
|
|
-- functions other modules are able to call
|
|
genesis_v2.process = process
|
|
|
|
-- return the module's table
|
|
return genesis_v2
|