class Gameboy: def __init__(self, bootrom, rom, skip_bootrom=False): self.timer = Timer() self.ppu = PPU() self.apu = APU() self.joypad = Joypad() self.cartridge = Cartridge(rom) self.mmu = MMU(bootrom, self.cartridge, self.timer, self.ppu, self.apu, self.joypad) self.timer.mmu = self.mmu self.ppu.mmu = self.mmu self.joypad.mmu = self.mmu self.cpu = CPU(self.mmu) self.t_cycles = 0 self.reset(skip_bootrom) #if skip_bootrom: # self.ppu.LY = 0x90 # for LY stubbed testing def reset(self, skip_bootrom=False): self.cpu.reset(skip_bootrom) self.mmu.reset(skip_bootrom) self.timer.reset(skip_bootrom) self.ppu.reset(skip_bootrom) self.joypad.reset() self.cartridge.reset() def run_frame(self): while not self.ppu.new_frame: self.cpu.handle_interrupts() cycles = self.cpu.tick() for i in range(cycles // 4): self.timer.tick() self.ppu.tick() self.t_cycles += cycles self.ppu.new_frame = False
def compile(code, output, path): cart = Cartridge() cart.path = path tokens = lexical(code) ast = syntax(tokens) opcodes = semantic(ast, True, cart) pynes.write_bin_code(opcodes, "output.nes")
def __init__(self, bootrom, rom, skip_bootrom=False): self.timer = Timer() self.ppu = PPU() self.apu = APU() self.joypad = Joypad() self.cartridge = Cartridge(rom) self.mmu = MMU(bootrom, self.cartridge, self.timer, self.ppu, self.apu, self.joypad) self.timer.mmu = self.mmu self.ppu.mmu = self.mmu self.joypad.mmu = self.mmu self.cpu = CPU(self.mmu) self.t_cycles = 0 self.reset(skip_bootrom)
def test_load(): cart = Cartridge('nestest.nes') assert cart.mapper_id == 0 assert cart.prg_banks == 1 assert cart.chr_banks == 1 assert len(cart.prg_memory) == 16384 assert len(cart.chr_memory) == 8192
def __init__(self, fname): self.cart = Cartridge(fname) self.mem = Memory() self.display = Display() self.cpu = CPU() # initial values for cpu self.cpu.set(PC=0x100, SP=0xFFFE, AF=0x01B1, BC=0x0013, DE=0x00D8, HL=0x014D) # set rom banking mode self.mem.set_rom_bank_mode(self.cart.get_rom_bank_mode_byte())
class Emulator(): FPS = 60 """ int, frames per second """ def __init__(self, fname): self.cart = Cartridge(fname) self.mem = Memory() self.display = Display() self.cpu = CPU() # initial values for cpu self.cpu.set(PC=0x100, SP=0xFFFE, AF=0x01B1, BC=0x0013, DE=0x00D8, HL=0x014D) # set rom banking mode self.mem.set_rom_bank_mode(self.cart.get_rom_bank_mode_byte()) def update(self): # operations to execute before a frame draw max_cycles = self.cpu.CLOCK_SPEED / self.FPS c = 0 while (c < max_cycles): c += self.cpu.execute_next_opcode() self.cpu.update_timers(c) # update_graphics(c) # do_interupts() self.display.render()
def __init__(self): # self.rom = "../data/loz.gb" # self.rom = "../data/test/individual/09-op r,r.gb" self.rom = "../test/simple-rom/simple.gb" self._cartridge = Cartridge(self.rom) self._codes = OpcodeParser() self._codes.load_instructions("./dat/opcodes.json") self._screen = Screen() self._mem = mbc1.MBC1(self._cartridge, self._screen) self._cpu = CPU(self._cartridge, self._mem, self._codes, self._screen) self._screen.set_cpu(self._cpu) self._cpu.run()
def on_init(self): pygame.init() self._display_surf = pygame.display.set_mode( self.size, pygame.HWSURFACE | pygame.DOUBLEBUF, 8) # Set NES color palette. self._display_surf.set_palette([(0x75, 0x75, 0x75), (0x27, 0x1b, 0x8f), (0x00, 0x00, 0xab), (0x47, 0x00, 0x9f), (0x8f, 0x00, 0x77), (0xab, 0x00, 0x13), (0xa7, 0x00, 0x00), (0x7f, 0x0b, 0x00), (0x43, 0x2f, 0x00), (0x00, 0x47, 0x00), (0x00, 0x51, 0x00), (0x00, 0x3f, 0x17), (0x1b, 0x3f, 0x5f), (0x00, 0x00, 0x00), (0x00, 0x00, 0x00), (0x00, 0x00, 0x00), (0xbc, 0xbc, 0xbc), (0x00, 0x73, 0xef), (0x23, 0x3b, 0xef), (0x83, 0x00, 0xf3), (0xbf, 0x00, 0xbf), (0xe7, 0x00, 0x5b), (0xdb, 0x2b, 0x00), (0xcb, 0x4f, 0x0f), (0x8b, 0x73, 0x00), (0x00, 0x97, 0x00), (0x00, 0xab, 0x00), (0x00, 0x93, 0x3b), (0x00, 0x83, 0x8b), (0x00, 0x00, 0x00), (0x00, 0x00, 0x00), (0x00, 0x00, 0x00), (0xff, 0xff, 0xff), (0x3f, 0xbf, 0xff), (0x5f, 0x97, 0xff), (0xa7, 0x8b, 0xfd), (0xf7, 0x7b, 0xff), (0xff, 0x77, 0xb7), (0xff, 0x77, 0x63), (0xff, 0x9b, 0x3b), (0xf3, 0xbf, 0x3f), (0x83, 0xd3, 0x13), (0x4f, 0xdf, 0x4b), (0x58, 0xf8, 0x98), (0x00, 0xeb, 0xdb), (0x00, 0x00, 0x00), (0x00, 0x00, 0x00), (0x00, 0x00, 0x00), (0xff, 0xff, 0xff), (0xab, 0xe7, 0xff), (0xc7, 0xd7, 0xff), (0xd7, 0xcb, 0xff), (0xff, 0xc7, 0xff), (0xff, 0xc7, 0xdb), (0xff, 0xbf, 0xb3), (0xff, 0xdb, 0xab), (0xff, 0xe7, 0xa3), (0xe3, 0xff, 0xa3), (0xab, 0xf3, 0xbf), (0xb3, 0xff, 0xcf), (0x9f, 0xff, 0xf3), (0x00, 0x00, 0x00), (0x00, 0x00, 0x00), (0x00, 0x00, 0x00)]) self._running = True self.cartridge = Cartridge("../../test/ff.nes") self.ppu = Ppu(self._display_surf) self.papu = Papu() self.cpu = Cpu(self.ppu, self.papu, self.cartridge, KeyboardController(self._display_surf)) self.cpu.power_on()
def main(): opt = parse_command_line_args() if opt.disassemble is not None: if opt.disassemble == "boot": opt.disassemble = opt.boot_rom binary = load_binary(opt.disassemble) print("Disassembly of %s\n" % os.path.relpath(opt.disassemble)) disassemble(binary, opt.start_address) sys.exit(0) if opt.cartridge is not None: log("Loading boot ROM from %s" % os.path.relpath(opt.boot_rom)) boot = load_binary(opt.boot_rom) log("Loading cartridge from %s" % os.path.relpath(opt.cartridge)) binary = load_binary(opt.cartridge) cartridge = Cartridge(binary) log(cartridge) log("Booting Gameboy") gameboy = Gameboy(cartridge, boot, no_display=opt.no_display, zoom=opt.zoom) if opt.skip_boot: set_boot(gameboy) gameboy.memory.boot_rom_active = False if opt.debug: Debugger(gameboy).run() sys.exit(0) else: try: gameboy.cpu.run() except EmulatorError as e: log("\n** Exception: %s" % str(e).strip()) Debugger(gameboy).run() except Exception as e: log("\n** Exception: %s" % str(e).strip()) gameboy.cpu.print_registers() log("") raise
def test_nesttest(expected): nes = Bus() cart = Cartridge('nestest.nes') nes.insert_cartridge(cart) nes.cpu_write(0xFFFC, 0x00) nes.reset() assert nes.cpu_read(0xFFFC) == 0x00 assert nes.cpu_read(0xFFFd) == 0xC0 while True: nes.clock() if nes.cpu.complete(): break mock_cpu = CPU(nes) results = {} cycle_errors = 0 test_line_count = len(expected) print(f'\n### Nestest lines={test_line_count} ###') for index, expected_line in enumerate(expected): mock_cpu.reset() pc = nes.cpu.state.pc a = nes.cpu.state.a & 0xFF x = nes.cpu.state.x y = nes.cpu.state.y p = nes.cpu.state.status sp = nes.cpu.state.stkp cycle_count = nes.cpu.state.clock_count ppu_cycle = nes.ppu._cycle ppu_scanline = nes.ppu._scanline opcode = nes.cpu.cpu_read(pc) mock_cpu.state.pc += 1 instruction = nes.cpu.OPCODES[opcode] instruction.addr_mode(mock_cpu) address = create_address_line(nes, mock_cpu, instruction) op_name = f'{instruction.name} {address}' op_bytes = f'{opcode:02X}' for data in mock_cpu.fetches: op_bytes += f' {data:02X}' ns = '*' if instruction.is_non_standard else ' ' result = (f'{pc:04X} {op_bytes:<9}{ns}{op_name:<32}A:{a:02X} ' f'X:{x:02X} Y:{y:02X} P:{p:02X} SP:{sp:02X} ' f'PPU:{ppu_scanline:>3},{ppu_cycle:>3} CYC:{cycle_count}') results[index] = result if not result[:78] == expected_line[:78]: print_context(results, index, expected_line, 25) mock_cpu.state.print_flags() if result[78:] != expected_line[78:]: cycle_errors += 1 error_msg = f'Line {index + 1} did not match the expected result!' assert result[:78] == expected_line[:78], error_msg # assert result == expected_line # strict (includes cycles, scanline and clock) try: clock(nes) except TypeError: msg = f'Operator {instruction.operate.__name__} not implemented!' print(red(msg)) print_context(results, index, expected_line) raise UserWarning(msg) error_1 = nes.cpu_read(0x0002) error_2 = nes.cpu_read(0x0003) msg = f'Error codes: 0x02 = 0x{error_1:02X} 0x03 = 0x{error_2:02X} (nestest.txt)' assert error_1 == 0x00 and error_2 == 0x00, msg print(f'Total number of cycle errors: {cycle_errors}')
import time from bus import Bus from cartridge import Cartridge if __name__ == '__main__': nes = Bus() # cart = Cartridge('tests/nestest.nes') cart = Cartridge('roms/donkeykong.nes') # cart = Cartridge('roms/smb.nes') # cart = Cartridge('roms/ducktales.nes') nes.insert_cartridge(cart) nes.reset() frame_count = 20 now = time.time() while True: nes.clock() if nes.ppu.frame_count == frame_count: break diff = time.time() - now print('------------') print(f'Ran for {frame_count} frames.') print(f'Running time: {diff:.{2}f} seconds') print(f'Frames per second: {frame_count / diff:.{3}f} FPS') print(f'Quality (60 FPS expected): {(frame_count / diff) / 60:.{2}f}') # Best so far: # 2020-10-06
def semantic(ast, iNES=False, cart=None): if cart == None: cart = Cartridge() labels = get_labels(ast) address = 0 # translate statments to opcode for leaf in ast: if leaf["type"] == "S_RS": labels[leaf["children"][0]["value"]] = cart.rs cart.rs += get_value(leaf["children"][2]) elif leaf["type"] == "S_DIRECTIVE": directive = leaf["children"][0]["value"] if len(leaf["children"]) == 2: argument = get_value(leaf["children"][1], labels) else: argument = leaf["children"][1:] if directive in directive_list: directive_list[directive](argument, cart) else: raise Exception("UNKNOW DIRECTIVE") else: if leaf["type"] in ["S_IMPLIED", "S_ACCUMULATOR"]: instruction = leaf["children"][0]["value"] address = False elif leaf["type"] == "S_RELATIVE": instruction = leaf["children"][0]["value"] address = get_value(leaf["children"][1], labels) elif leaf["type"] == "S_IMMEDIATE_WITH_MODIFIER": instruction = leaf["children"][0]["value"] modifier = leaf["children"][1]["value"] address = get_value(leaf["children"][3], labels) if modifier == "#LOW": address = address & 0x00FF elif modifier == "#HIGH": address = (address & 0xFF00) >> 8 elif leaf["type"] in [ "S_RELATIVE", "S_IMMEDIATE", "S_ZEROPAGE", "S_ABSOLUTE", "S_ZEROPAGE_X", "S_ZEROPAGE_Y", "S_ABSOLUTE_X", "S_ABSOLUTE_Y", ]: instruction = leaf["children"][0]["value"] address = get_value(leaf["children"][1], labels) elif leaf["type"] in ["S_INDIRECT_X", "S_INDIRECT_Y"]: instruction = leaf["children"][0]["value"] address = get_value(leaf["children"][2], labels) address_mode = address_mode_def[leaf["type"]]["short"] opcode = opcodes[instruction][address_mode] if address_mode != "sngl": if "rel" == address_mode: address = 126 + (address - cart.pc) if address == 128: address = 0 elif address < 128: address = address | 0b10000000 elif address > 128: address = address & 0b01111111 if address_mode_def[leaf["type"]]["size"] == 2: cart.append_code([opcode, address]) else: arg1 = address & 0x00FF arg2 = (address & 0xFF00) >> 8 cart.append_code([opcode, arg1, arg2]) else: cart.append_code([opcode]) nes_code = [] if iNES: return cart.get_ines_code() else: return cart.get_code()
def load_cartridge(self, filename): self.cartridge = Cartridge(filename)
def main(fn): cartridge = Cartridge(fn) try: os.mkdir("dump") except: pass print "============== Audio data ======================" addr = 5 n = 1 while 1: a = cartridge.read_int32(addr) a = a & 0xffffff l = cartridge.read_int32(a) if l == 0: break format = cartridge.read_int16(a+4) print "Audio%05d: %06X %d %s" % (n, a, l, format) f = open("dump/%05d.aud" % n,"wb") f.write(cartridge.data[a+4:a+l+4]) f.close() addr += 3 n += 1 index_base = cartridge.read_int16(2) mover_scripts_end = cartridge.read_int16(index_base+2) mover_scripts = cartridge.read_int16(index_base+4) mover_scripts_spi = mover_scripts * 2 + index_base mover_scripts_num = cartridge.read_int16(mover_scripts_spi - 2) cx_offset = cartridge.read_int16(index_base+6) addrs = {} aliases = {} for i in range(mover_scripts_num): addr = cartridge.read_int16(mover_scripts_spi + i*2) if addr in addrs: if not addr in aliases: aliases[addr] = [] aliases[addr].append(i) else: addrs[addr] = i addrs_keys = addrs.keys() addrs_keys.sort() if 0: for addr in addrs_keys: if addr in aliases: aliases_str = " aliases: " +",".join([ "%d" % a for a in aliases[addr]]) else: aliases_str = "" print "Script%05d: %04X (%06X)%s" % (addrs[addr], addr, index_base + addr*2, aliases_str) print "============== Mover Scripts ======================" addr = addrs_keys[0] while addr<mover_scripts_end: if addr in addrs: print "scr%d:" % addrs[addr] if addr in aliases: for a in aliases[addr]: print "scr%d:" % a spi_addr = addr*2+index_base cmd = cartridge.read_int16(spi_addr) if (cmd & 0xF000) == 0xF000: if cmd == 0xF000: cmd_txt = "done" elif cmd == 0xF001: cmd_txt = "ctrl_F001" elif cmd == 0xF002: cmd_txt = "wait_completion" elif cmd == 0xF003: arg = cartridge.read_int16(spi_addr+2) cmd_txt = "wait_completion_max %d" % arg addr += 1 elif cmd == 0xF004: cmd_txt = "wait_04" elif cmd == 0xF005: arg = cartridge.read_int16(spi_addr+2) cmd_txt = "wait_completion_max %d" % arg addr += 1 elif cmd == 0xF006: arg = cartridge.read_int16(spi_addr+2) if arg in addrs: label = "scr%d" % addrs[arg] else: label = "%04X" % arg cmd_txt = "call %s" % label addr += 1 elif cmd == 0xF007: cmd_txt = "ctrl_F007" elif cmd == 0xF008: cmd_txt = "ctrl_F008" elif cmd == 0xF009: cmd_txt = "ctrl_F009" elif cmd == 0xF00A: cmd_txt = "return" elif cmd == 0xF00B: cmd_txt = "ctrl_F00B" elif cmd == 0xF00C: cmd_txt = "ctrl_F00C" elif cmd == 0xF00D: cmd_txt = "wait_0D" elif cmd == 0xF00E: cmd_txt = "ctrl_F00E" elif cmd == 0xF00F: cmd_txt = "ctrl_F00F" else: cmd_txt = "ctrl_%04X" % cmd else: cmd_txt = "cmd_%04X" % cmd print "\t%04X: %s" % (addr, cmd_txt) addr += 1 xrefs_code_local = {} xrefs_code_global = {} xrefs_data = {} xrefs_globals = {} barriers = {} labels = {} def add_xref(addr, typ, ref): #print "add xref %s %04X -> %04X" % (typ, addr, ref) if typ == "code_local": xrefs = xrefs_code_local elif typ == "code_global": xrefs = xrefs_code_global elif typ == "data": xrefs = xrefs_data elif typ == "globals": xrefs = xrefs_globals else: return if not ref in xrefs: xrefs[ref] = {} xrefs[ref][addr] = 1 barriers_list = [] print "=================== Code =======================" for pass_num in (1,2): addr = 0x20 cpu = CPU(cartridge, index_base, cx_offset, addr, None, None) cpu.labels = labels barriers[addr] = 1 while addr < mover_scripts-1: spi_addr = addr * 2 + index_base decoded = cpu.decode(addr) if decoded.barrier: barriers[addr+1] = 1 for typ, ref in decoded.xrefs: add_xref(addr,typ, ref) hex = [] for i in range(decoded.cmd_len): hex.append("%04X" % cartridge.read_int16(spi_addr + i * 2)) hex = " ".join(hex) if pass_num == 2: if addr in barriers_list: print "//------------------------------------------------------------------" if addr in xrefs_code_global: print "// global entry, xrefs: ",",".join(["%04X"%d for d in xrefs_code_global[addr]]) if addr in xrefs_data: print "// data entry, xrefs: ",",".join(["%04X"%d for d in xrefs_data[addr]]) if addr in labels: print " %20s: // xrefs: %s" % (labels[addr],",".join(["%04X"%d for d in xrefs_code_local[addr]])) print "%04X: %-20s %-9s %-20s // %s" % (addr, hex, decoded.cmd, decoded.args or "", decoded.descr or "") addr += decoded.cmd_len barriers[addr] = 1 if pass_num == 1: barriers_list = barriers.keys() barriers_list.sort() #print "barriers", barriers_list[:10] for ref in xrefs_code_local: ref_idx = bisect.bisect(barriers_list,ref) - 1 #print "barrs", ref_idx, ref, barriers_list[ref_idx] for addr in xrefs_code_local[ref]: addr_idx = bisect.bisect(barriers_list,addr) - 1 if ref_idx != addr_idx: #print "cross area jump: %04X %04X %d %d" % (addr, ref, addr_idx, ref_idx) if ref_idx<addr_idx: start_idx = ref_idx end_idx = addr_idx else: start_idx = addr_idx end_idx = ref_idx if end_idx - start_idx<10: for idx in range(start_idx,end_idx): try: #print "delete barrier", barriers_list[start_idx+1] del barriers_list[start_idx+1] except IndexError: pass else: print "%04X: too long cross area jump %d -> %d, ignoring" % (addr, addr_idx, ref_idx) else: #print "intra area jump: %04X %04X %d %d" % (addr, ref, addr_idx, ref_idx) pass labels = {} refs = xrefs_code_local.keys() refs.sort() last_area = -1 label_cnt = 0 for ref in refs: ref_idx = bisect.bisect(barriers_list,ref) - 1 if ref_idx != last_area: label_cnt = 1 last_area = ref_idx labels[ref] = "L%d_%d" % (ref_idx, label_cnt) label_cnt += 1 print "=================== Globals =======================" refs = xrefs_globals.keys() refs.sort() for ref in refs: print "%04X %s" % (ref,",".join(["%04X"%d for d in xrefs_globals[ref].keys()]))