Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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")
Ejemplo n.º 3
0
 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)
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
    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())
Ejemplo n.º 6
0
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()
Ejemplo n.º 7
0
    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()
Ejemplo n.º 8
0
    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()
Ejemplo n.º 9
0
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
Ejemplo n.º 10
0
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}')
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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()
Ejemplo n.º 13
0
 def load_cartridge(self, filename):
     self.cartridge = Cartridge(filename)
Ejemplo n.º 14
0
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()]))