rom = [0x00] * 256
rom[0x00:0x03] = [0x31, 0xFE, 0xFF]
rom[0x03:0x0C] = [0x21, 0x00, 0x80, 0xAF, 0x22, 0xCB, 0x6C, 0x28, 0xFB]
rom[0x0C:0x10] = [0x3E, 0x80, 0xE0, 0x26] rom[0x10:0x12] = [0xE0, 0x11] rom[0x12:0x16] = [0x3E, 0xF3, 0xE0, 0x12] rom[0x16:0x18] = [0xE0, 0x25] rom[0x18:0x1C] = [0x3E, 0x77, 0xE0, 0x24]
rom[0x1C:0x20] = [0x3E, 0xFC, 0xE0, 0x47]
rom[0x20:0x23] = [0x11, 0x04, 0x01] rom[0x23:0x26] = [0x21, 0x10, 0x80] rom[0x26:0x28] = [0x1A, 0x47] rom[0x28:0x2B] = [0xCD, 0xA7, 0x00] rom[0x2B:0x2E] = [0xCD, 0xA7, 0x00] rom[0x2E] = 0x13 rom[0x2F:0x32] = [0x7B, 0xEE, 0x34] rom[0x32:0x34] = [0x20, 0xF2]
rom[0x34:0x36] = [0x3E, 0x19] rom[0x36:0x39] = [0x21, 0x2F, 0x99] rom[0x39:0x3B] = [0x0E, 0x0C] rom[0x3B] = 0x3D rom[0x3C:0x3E] = [0x28, 0x08] rom[0x3E] = 0x32 rom[0x3F] = 0x0D rom[0x40:0x42] = [0x20, 0xF9] rom[0x42:0x44] = [0x2E, 0x0F] rom[0x44:0x46] = [0x18, 0xF5]
rom[0x46:0x49] = [0x21, 0xFE, 0x07] rom[0x49:0x4B] = [0x00, 0x00] rom[0x4B:0x50] = [0x2B, 0x7C, 0xB5, 0x20, 0xFB]
rom[0x50:0x54] = [0x3E, 0x91, 0xE0, 0x40]
rom[0x54:0x58] = [0x3E, 0x64, 0xE0, 0x42]
rom[0x58] = 0x57 rom[0x59] = 0x04 rom[0x5A:0x5C] = [0x26, 0x00]
LOOP = 0x5C
rom[0x5C:0x5E] = [0x1E, 0x02]
WAIT_NOT_VB = 0x5E
rom[0x5E:0x60] = [0xF0, 0x44] rom[0x60:0x62] = [0xFE, 0x90] offset = WAIT_NOT_VB - (0x64) rom[0x62:0x64] = [0x30, offset & 0xFF]
WAIT_VB = 0x64
rom[0x64:0x66] = [0xF0, 0x44] rom[0x66:0x68] = [0xFE, 0x90] offset = WAIT_VB - (0x6A) rom[0x68:0x6A] = [0x38, offset & 0xFF]
rom[0x6A] = 0x1D offset = WAIT_NOT_VB - (0x6D) rom[0x6B:0x6D] = [0x20, offset & 0xFF]
rom[0x6D] = 0x24 rom[0x6E] = 0x7C rom[0x6F:0x71] = [0x0E, 0x13] rom[0x71:0x73] = [0x1E, 0x83] rom[0x73:0x75] = [0xFE, 0x62]
PLAY_SOUND = 0x7D
offset = PLAY_SOUND - (0x77) rom[0x75:0x77] = [0x28, offset & 0xFF]
rom[0x77:0x79] = [0x1E, 0xC1] rom[0x79:0x7B] = [0xFE, 0x64]
NO_SOUND = 0x85
offset = NO_SOUND - (0x7D) rom[0x7B:0x7D] = [0x20, offset & 0xFF]
rom[0x7D] = 0x7B rom[0x7E] = 0xE2 rom[0x7F] = 0x0C rom[0x80:0x82] = [0x3E, 0x87] rom[0x82] = 0xE2
offset = NO_SOUND - (0x85) rom[0x83:0x85] = [0x18, offset & 0xFF]
rom[0x85:0x87] = [0xF0, 0x42] rom[0x87] = 0x90 rom[0x88:0x8A] = [0xE0, 0x42]
rom[0x8A] = 0x15 offset = LOOP - (0x8D) rom[0x8B:0x8D] = [0x20, offset & 0xFF]
rom[0x8D] = 0x05
DONE = 0xBC offset = DONE - (0x90) rom[0x8E:0x90] = [0x20, offset & 0xFF]
rom[0x90:0x92] = [0x16, 0x20] offset = LOOP - (0x94) rom[0x92:0x94] = [0x18, offset & 0xFF]
rom[0xA7:0xA9] = [0x3E, 0x04] rom[0xA9:0xAB] = [0x0E, 0x00] rom[0xAB:0xAD] = [0xCB, 0x20] rom[0xAD] = 0xF5 rom[0xAE:0xB0] = [0xCB, 0x11] rom[0xB0] = 0xF1 rom[0xB1:0xB3] = [0xCB, 0x11] rom[0xB3] = 0x3D rom[0xB4:0xB6] = [0x20, 0xF5] rom[0xB6] = 0x79 rom[0xB7:0xB9] = [0x22, 0x23] rom[0xB9:0xBB] = [0x22, 0x23] rom[0xBB] = 0xC9
rom[0xBC:0xC0] = [0x3E, 0xE1, 0xE0, 0x0F]
rom[0xC0:0xC3] = [0x21, 0x00, 0x00] rom[0xC3:0xC5] = [0x00, 0x00] rom[0xC5:0xCA] = [0x2B, 0x7C, 0xB5, 0x20, 0xFB]
rom[0xCA:0xCD] = [0x21, 0xB0, 0x01] rom[0xCD:0xCF] = [0xE5, 0xF1] rom[0xCF:0xD2] = [0x21, 0x4D, 0x01] rom[0xD2:0xD5] = [0x01, 0x13, 0x00] rom[0xD5:0xD8] = [0x11, 0xD8, 0x00] rom[0xD8:0xDB] = [0xC3, 0xFE, 0x00]
rom[0xFE:0x100] = [0xE0, 0x50]
print("ROM assembled successfully!")
print(f"Total non-zero bytes: {sum(1 for b in rom if b != 0)}")
print(f"Scroll section: $005C-$0093 ({0x94-0x5C} bytes)")
print(f"Post-loop: $00BC-$00DA ({0xDB-0xBC} bytes)")
print()
def check_jr(addr, target, name):
opcode = rom[addr]
offset_byte = rom[addr + 1]
signed_offset = offset_byte if offset_byte < 128 else offset_byte - 256
actual_target = addr + 2 + signed_offset
ok = "OK" if actual_target == target else f"FAIL (goes to ${actual_target:04X})"
print(f" JR at ${addr:04X} → ${target:04X}: offset={signed_offset:+d} (${offset_byte:02X}) {ok}")
assert actual_target == target, f"{name}: expected ${target:04X}, got ${actual_target:04X}"
print("Jump offset verification:")
check_jr(0x32, 0x26, "logoLoop")
check_jr(0x3C, 0x46, "tmapDone")
check_jr(0x40, 0x3B, "tmapLoop NZ")
check_jr(0x44, 0x3B, "tmapLoop JR")
check_jr(0x4E, 0x4B, "preLoop")
check_jr(0x62, 0x5E, "waitNotVblank NC")
check_jr(0x68, 0x64, "waitVblank C")
check_jr(0x6B, 0x5E, "waitNotVblank NZ")
check_jr(0x75, 0x7D, "playSound Z")
check_jr(0x7B, 0x85, "noSound NZ")
check_jr(0x83, 0x85, "noSound JR")
check_jr(0x8B, 0x5C, "loop NZ")
check_jr(0x8E, 0xBC, "done NZ")
check_jr(0x92, 0x5C, "loop JR")
check_jr(0xC8, 0xC5, "fineLoop")
print()
print("Test assertion verification:")
assert rom[0x10] == 0xE0 and rom[0x11] == 0x11, "NR11 write"
print(" ✓ NR11 duty cycle write present")
apu_init = rom[0x0C:0x1C]
assert not any(apu_init[i] == 0xE0 and apu_init[i+1] == 0x14 for i in range(len(apu_init)-1))
assert not any(apu_init[i] == 0xE0 and apu_init[i+1] == 0x13 for i in range(len(apu_init)-1))
print(" ✓ No NR13/NR14 trigger in APU init")
assert rom[0x54:0x58] == [0x3E, 0x64, 0xE0, 0x42]
print(" ✓ SCY starts at $64 (100)")
scroll = rom[0x5A:0xFE]
assert any(scroll[i] == 0xF0 and scroll[i+1] == 0x44 for i in range(len(scroll)-1))
print(" ✓ LY polling (F0 44) present")
assert 0x83 in scroll and 0xC1 in scroll
print(" ✓ Two note frequencies ($83, $C1) present")
assert 0x62 in scroll and 0x64 in scroll
print(" ✓ Trigger thresholds ($62, $64) present")
assert any(scroll[i] == 0x16 and scroll[i+1] == 0x20 for i in range(len(scroll)-1))
print(" ✓ Hold phase LD D,$20 (32 iterations) present")
print()
print("All checks passed!")
print()
print("Rust array bytes:")
for i in range(0, 256, 16):
chunk = rom[i:i+16]
hex_vals = ', '.join(f'0x{b:02X}' for b in chunk)
addr_end = min(i + 15, 255)
print(f' {hex_vals}, // ${i:04X}–${addr_end:04X}')