mirror of https://github.com/zeldaret/mm.git
New Romheader format (#1628)
This commit is contained in:
parent
c609d3dcae
commit
de69d62800
2
Makefile
2
Makefile
|
@ -417,7 +417,7 @@ $(BUILD_DIR)/src/boot/z_std_dma.o: $(BUILD_DIR)/dmadata_table_spec.h
|
||||||
$(BUILD_DIR)/src/dmadata/dmadata.o: $(BUILD_DIR)/dmadata_table_spec.h
|
$(BUILD_DIR)/src/dmadata/dmadata.o: $(BUILD_DIR)/dmadata_table_spec.h
|
||||||
|
|
||||||
$(BUILD_DIR)/asm/%.o: asm/%.s
|
$(BUILD_DIR)/asm/%.o: asm/%.s
|
||||||
$(AS) $(ASFLAGS) $< -o $@
|
$(CPP) $(CPPFLAGS) -Iinclude $< | $(AS) $(ASFLAGS) -o $@
|
||||||
|
|
||||||
$(BUILD_DIR)/assets/%.o: assets/%.c
|
$(BUILD_DIR)/assets/%.o: assets/%.c
|
||||||
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
|
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
#ifndef ROM_HEADER_H
|
||||||
|
#define ROM_HEADER_H
|
||||||
|
|
||||||
|
/* Storage medium IDs, used internally in MEDIUM below */
|
||||||
|
|
||||||
|
#define STORAGE_MEDIUM_CARTRIDGE "N"
|
||||||
|
#define STORAGE_MEDIUM_CARTRIDGE_EXPANDABLE "C"
|
||||||
|
#define STORAGE_MEDIUM_DISK "D"
|
||||||
|
#define STORAGE_MEDIUM_DISK_EXPANSION "E"
|
||||||
|
|
||||||
|
/* Region IDs, used internally in REGION below */
|
||||||
|
|
||||||
|
#define REGION_CODE_ALL "A"
|
||||||
|
#define REGION_CODE_JP "J"
|
||||||
|
#define REGION_CODE_US "E"
|
||||||
|
#define REGION_CODE_PAL "P"
|
||||||
|
#define REGION_CODE_GATEWAY "G"
|
||||||
|
#define REGION_CODE_LODGENET "L"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magic value to determine if the ROM is byteswapped.
|
||||||
|
*
|
||||||
|
* This is not totally reliable since the PI Domain 1 Latency could also hold this value, however generally it can be
|
||||||
|
* assumed that this is not the case.
|
||||||
|
*/
|
||||||
|
#define ENDIAN_IDENTIFIER \
|
||||||
|
.byte 0x80
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the timings for PI Domain 1. This determines how fast the PI hardware will read from the ROM. IPL2 reads
|
||||||
|
* this configuration using the slowest possible configuration before switching to the provided configuration.
|
||||||
|
*/
|
||||||
|
#define PI_DOMAIN_1_CFG(lat, pwd, pgs, rls) \
|
||||||
|
.byte (((rls) & 3) << 4) | ((pgs) & 0xF); \
|
||||||
|
.byte (pwd) & 0xFF; \
|
||||||
|
.byte (lat) & 0xFF
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some older libultra versions use this field to set osClockRate. It does not have any other meaningful effect, the
|
||||||
|
* clock rate of physical units does not actually change to match this value.
|
||||||
|
*/
|
||||||
|
#define SYSTEM_CLOCK_RATE_SETTING(num) \
|
||||||
|
.word (num)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the entrypoint address of the program that IPL3 will load the boot segment to and execute.
|
||||||
|
* This should be >= 0x80000400.
|
||||||
|
*/
|
||||||
|
#define ENTRYPOINT(sym) \
|
||||||
|
.word (sym)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the hardware revision the program is designed for (hw_major, hw_minor)
|
||||||
|
* and what libultra version (os_ver) it uses.
|
||||||
|
*
|
||||||
|
* The hardware revision for a retail N64 is (2,0).
|
||||||
|
* The libultra version may be a single letter, without quotes.
|
||||||
|
*/
|
||||||
|
#define LIBULTRA_VERSION(hw_major, hw_minor, os_ver) \
|
||||||
|
.half 0; \
|
||||||
|
.byte (hw_major) * 10 + (hw_minor); \
|
||||||
|
_os_ver_start = .; \
|
||||||
|
.ascii #os_ver ; \
|
||||||
|
.if (. - _os_ver_start) != 1; \
|
||||||
|
.error "OS version should be just one letter"; \
|
||||||
|
.endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leaves space to insert the ROM Checksum value. IPL3 computes a checksum over ROM data in the range 0x1000 to 0x101000
|
||||||
|
* and compares it to this value, if the results differ it will refuse to boot the program.
|
||||||
|
*
|
||||||
|
* This macro just writes 8 bytes of 0. The correct checksum value is filled in after the full ROM image is available to
|
||||||
|
* compute the checksum with.
|
||||||
|
*/
|
||||||
|
#define CHECKSUM() \
|
||||||
|
.word 0, 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For unused header space. Fills num bytes with 0.
|
||||||
|
*/
|
||||||
|
#define PADDING(num) \
|
||||||
|
.fill (num)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the ROM name. This should be a string that is at most 20 characters long, a null terminator is not required.
|
||||||
|
* If a name is less than 20 characters, the remaining space will be padded with spaces.
|
||||||
|
*/
|
||||||
|
#define ROM_NAME(name) \
|
||||||
|
_name_start = .; \
|
||||||
|
.ascii name; \
|
||||||
|
.if (. - _name_start) > 20; \
|
||||||
|
.error "ROM name too long, must be at most 20 characters"; \
|
||||||
|
.endif; \
|
||||||
|
.if (. - _name_start) < 20; \
|
||||||
|
.fill 20 - (. - _name_start), 1, 0x20; \
|
||||||
|
.endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifies the storage medium the program intends to use.
|
||||||
|
*
|
||||||
|
* Should be one of:
|
||||||
|
* - CARTRIDGE
|
||||||
|
* - CARTRIDGE_EXPANDABLE
|
||||||
|
* - DISK
|
||||||
|
* - DISK_EXPANSION
|
||||||
|
*/
|
||||||
|
#define MEDIUM(type) \
|
||||||
|
.ascii STORAGE_MEDIUM_##type
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Two-letter game identifier. Should be wrapped in quotes.
|
||||||
|
*/
|
||||||
|
#define GAME_ID(id) \
|
||||||
|
_game_id_start = .; \
|
||||||
|
.ascii id ; \
|
||||||
|
.if (. - _game_id_start) != 2; \
|
||||||
|
.error "Game ID should be two letters"; \
|
||||||
|
.endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifies the region the game is made for.
|
||||||
|
*
|
||||||
|
* Should be one of:
|
||||||
|
* - ALL
|
||||||
|
* - JP
|
||||||
|
* - US
|
||||||
|
* - PAL
|
||||||
|
* - GATEWAY
|
||||||
|
* - LODGENET
|
||||||
|
*
|
||||||
|
* Note: Often flashcarts and emulators will read this value to determine whether to act as an NTSC or PAL system and
|
||||||
|
* will adjust the VI timings appropriately, which may be used to determine target FPS.
|
||||||
|
* This can lead to glitchy video output on hardware if the program configures a video mode other than the native
|
||||||
|
* video mode for the hardware version.
|
||||||
|
*/
|
||||||
|
#define REGION(name) \
|
||||||
|
.ascii REGION_CODE_##name
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifies the game revision number. Can be between 0 and 255.
|
||||||
|
*/
|
||||||
|
#define GAME_REVISION(num) \
|
||||||
|
.byte (num)
|
||||||
|
|
||||||
|
#endif
|
|
@ -1756,16 +1756,48 @@ def get_overlay_sections(vram, overlay):
|
||||||
[bss_end_vram, bss_end_vram, "reloc", None, overlay[header_loc:], None],
|
[bss_end_vram, bss_end_vram, "reloc", None, overlay[header_loc:], None],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_storage_medium(id):
|
||||||
|
if id == 'N':
|
||||||
|
return "CARTRIDGE"
|
||||||
|
elif id == 'C':
|
||||||
|
return "CARTRIDGE_EXPANDABLE"
|
||||||
|
elif id == 'D':
|
||||||
|
return "DISK"
|
||||||
|
elif id == 'E':
|
||||||
|
return "DISK_EXPANSION"
|
||||||
|
else:
|
||||||
|
return "UNKNOWN"
|
||||||
|
|
||||||
|
def get_region(id):
|
||||||
|
if id == 'A':
|
||||||
|
return "ALL"
|
||||||
|
elif id == 'J':
|
||||||
|
return "JP"
|
||||||
|
elif id == 'E':
|
||||||
|
return "US"
|
||||||
|
elif id == 'P':
|
||||||
|
return "PAL"
|
||||||
|
elif id == 'G':
|
||||||
|
return "GATEWAY"
|
||||||
|
elif id == 'L':
|
||||||
|
return "LODGENET"
|
||||||
|
else:
|
||||||
|
return "UNKNOWN"
|
||||||
|
|
||||||
def disassemble_makerom(section):
|
def disassemble_makerom(section):
|
||||||
os.makedirs(f"{ASM_OUT}/makerom/", exist_ok=True)
|
os.makedirs(f"{ASM_OUT}/makerom/", exist_ok=True)
|
||||||
|
|
||||||
if section[2] == "rom_header":
|
if section[2] == "rom_header":
|
||||||
(
|
(
|
||||||
|
endian,
|
||||||
pi_dom1_reg,
|
pi_dom1_reg,
|
||||||
|
pi_dom1_pwd,
|
||||||
|
pi_dom1_lat,
|
||||||
clockrate,
|
clockrate,
|
||||||
entrypoint,
|
entrypoint,
|
||||||
revision,
|
pad_ver,
|
||||||
|
hw_ver,
|
||||||
|
os_ver,
|
||||||
chksum1,
|
chksum1,
|
||||||
chksum2,
|
chksum2,
|
||||||
pad1,
|
pad1,
|
||||||
|
@ -1776,26 +1808,28 @@ def disassemble_makerom(section):
|
||||||
cart_id,
|
cart_id,
|
||||||
region,
|
region,
|
||||||
version,
|
version,
|
||||||
) = struct.unpack(">IIIIIIII20sII2s1sB", section[4])
|
) = struct.unpack(">BBBBIIHBBIIII20sII2s1sB", section[4])
|
||||||
|
|
||||||
|
|
||||||
out = f"""/*
|
out = f"""/*
|
||||||
* The Legend of Zelda: Majora's Mask ROM header
|
* The Legend of Zelda: Majora's Mask ROM header
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.word 0x{pi_dom1_reg:08X} /* PI BSD Domain 1 register */
|
#include "rom_header.h"
|
||||||
.word 0x{clockrate:08X} /* Clockrate setting */
|
|
||||||
.word 0x{entrypoint:08X} /* Entrypoint function (`entrypoint`) */
|
/* 0x00 */ ENDIAN_IDENTIFIER
|
||||||
.word 0x{revision:08X} /* Revision */
|
/* 0x01 */ PI_DOMAIN_1_CFG({pi_dom1_lat}, {pi_dom1_pwd}, {pi_dom1_reg & 0xF}, {(pi_dom1_reg >> 4) & 3})
|
||||||
.word 0x{chksum1:08X} /* Checksum 1 */
|
/* 0x04 */ SYSTEM_CLOCK_RATE_SETTING(0x{clockrate:X})
|
||||||
.word 0x{chksum2:08X} /* Checksum 2 */
|
/* 0x08 */ ENTRYPOINT(0x{entrypoint:08X})
|
||||||
.word 0x{pad1:08X} /* Unknown */
|
/* 0x0C */ LIBULTRA_VERSION({hw_ver // 10}, {hw_ver % 10}, {chr(os_ver)})
|
||||||
.word 0x{pad2:08X} /* Unknown */
|
/* 0x10 */ CHECKSUM()
|
||||||
.ascii "{rom_name.decode('ascii')}" /* Internal ROM name */
|
/* 0x18 */ PADDING(8)
|
||||||
.word 0x{pad3:08X} /* Unknown */
|
/* 0x20 */ ROM_NAME("{rom_name.decode('ascii').rstrip()}")
|
||||||
.word 0x{cart:08X} /* Cartridge */
|
/* 0x34 */ PADDING(7)
|
||||||
.ascii "{cart_id.decode('ascii')}" /* Cartridge ID */
|
/* 0x3B */ MEDIUM({get_storage_medium(chr(cart))})
|
||||||
.ascii "{region.decode('ascii')}" /* Region */
|
/* 0x3C */ GAME_ID("{cart_id.decode('ascii')}")
|
||||||
.byte 0x{version:02X} /* Version */
|
/* 0x3E */ REGION({get_region(region.decode('ascii'))})
|
||||||
|
/* 0x3F */ GAME_REVISION({version})
|
||||||
"""
|
"""
|
||||||
with open(ASM_OUT + "/makerom/rom_header.s", "w") as outfile:
|
with open(ASM_OUT + "/makerom/rom_header.s", "w") as outfile:
|
||||||
outfile.write(out)
|
outfile.write(out)
|
||||||
|
|
Loading…
Reference in New Issue