Пример #1
0
    def setregister(self, register, value):
        if not self.latch_enabled:
            logger.info(
                "RTC: Set register, but nothing is latched! 0x%0.4x, 0x%0.2x" %
                (register, value))

        t = time.time() - self.timezero
        if register == 0x08:
            # TODO: What happens, when these value are larger than allowed?
            self.timezero -= int(t % 60) - value
        elif register == 0x09:
            self.timezero -= int(t / 60 % 60) - value
        elif register == 0x0A:
            self.timezero -= int(t / 3600 % 24) - value
        elif register == 0x0B:
            self.timezero -= int(t / 3600 / 24) - value
        elif register == 0x0C:
            day_high = value & 0b1
            halt = (value & 0b1000000) >> 6
            day_carry = (value & 0b10000000) >> 7

            self.halt = halt
            if self.halt == 0:
                pass  # TODO: Start the timer
            else:
                logger.warning("Stopping RTC is not implemented!")

            self.timezero -= int(t / 3600 / 24) - (day_high << 8)
            self.day_carry = day_carry
        else:
            logger.warning("Invalid RTC register: %0.4x %0.2x" %
                           (register, value))
Пример #2
0
    def __init__(self, filename, rombanks, external_ram_count, carttype, sram,
                 battery, rtc_enabled):
        self.filename = filename + ".ram"
        self.rombanks = rombanks
        self.carttype = carttype

        self.battery = battery
        self.rtc_enabled = rtc_enabled

        if self.rtc_enabled:
            self.rtc = RTC(filename)

        self.rambank_initialized = False
        self.external_ram_count = external_ram_count
        self.init_rambanks(external_ram_count)
        self.gamename = self.getgamename(rombanks)

        self.memorymodel = 0
        self.rambank_enabled = False
        self.rambank_selected = 0  # TODO: Check this, not documented
        # Note: TestROM 01-special.gb assumes initial value of 1
        self.rombank_selected = 1  # TODO: Check this, not documented

        if not os.path.exists(self.filename):
            logger.info("No RAM file found. Skipping.")
        else:
            with open(self.filename, "rb") as f:
                self.load_ram(IntIOWrapper(f))
Пример #3
0
 def _unpause(self):
     if not self.paused:
         return
     self.paused = False
     self.target_emulationspeed = self.save_target_emulationspeed
     logger.info("Emulation unpaused!")
     self._update_window_title()
Пример #4
0
    def handle_events(self, events):
        old_rewind_speed = self.rewind_speed
        for event in events:
            if event == WindowEvent.UNPAUSE:
                self.rewind_buffer.commit()
            elif event == WindowEvent.PAUSE_TOGGLE:
                if self.pyboy.paused:
                    self.rewind_buffer.commit()
            elif event == WindowEvent.RELEASE_REWIND_FORWARD:
                self.rewind_speed = 1
            elif event == WindowEvent.PRESS_REWIND_FORWARD:
                self.pyboy._pause()
                if self.rewind_buffer.seek_frame(1):
                    self.mb.load_state(self.rewind_buffer)
                    events.append(WindowEvent._INTERNAL_RENDERER_FLUSH)
                    self.rewind_speed = min(self.rewind_speed * 1.1, 5)
                else:
                    logger.info("Rewind limit reached")
            elif event == WindowEvent.RELEASE_REWIND_BACK:
                self.rewind_speed = 1
            elif event == WindowEvent.PRESS_REWIND_BACK:
                self.pyboy._pause()
                if self.rewind_buffer.seek_frame(-1):
                    self.mb.load_state(self.rewind_buffer)
                    events.append(WindowEvent._INTERNAL_RENDERER_FLUSH)
                    self.rewind_speed = min(self.rewind_speed * 1.1, 5)
                else:
                    logger.info("Rewind limit reached")

        if old_rewind_speed != self.rewind_speed:
            # NOTE: Disable this line, if recording for .replay files
            self.pyboy.set_emulation_speed(int(self.rewind_speed))
        return events
Пример #5
0
 def _handle_events(self, events):
     # This feeds events into the tick-loop from the window. There might already be events in the list from the API.
     events = self.plugin_manager.handle_events(events)
     for event in events:
         if event == WindowEvent.QUIT:
             self.done = True
         elif event == WindowEvent.RELEASE_SPEED_UP:
             # Switch between unlimited and 1x real-time emulation speed
             self.target_emulationspeed = int(bool(self.target_emulationspeed) ^ True)
             logger.info("Speed limit: %s" % self.target_emulationspeed)
         elif event == WindowEvent.STATE_SAVE:
             with open(self.gamerom_file + ".state", "wb") as f:
                 self.mb.save_state(IntIOWrapper(f))
         elif event == WindowEvent.STATE_LOAD:
             state_path = self.gamerom_file + ".state"
             if not os.path.isfile(state_path):
                 logger.error(f"State file not found: {state_path}")
                 continue
             with open(state_path, "rb") as f:
                 self.mb.load_state(IntIOWrapper(f))
         elif event == WindowEvent.PASS:
             pass # Used in place of None in Cython, when key isn't mapped to anything
         elif event == WindowEvent.PAUSE_TOGGLE:
             if self.paused:
                 self._unpause()
             else:
                 self._pause()
         elif event == WindowEvent.PAUSE:
             self._pause()
         elif event == WindowEvent.UNPAUSE:
             self._unpause()
         elif event == WindowEvent._INTERNAL_RENDERER_FLUSH:
             self.plugin_manager._post_tick_windows()
         else:
             self.mb.buttonevent(event)
Пример #6
0
def test_rom(rom):
    logger.info(rom)
    pyboy = PyBoy("dummy", 1, rom, "ROMs/DMG_ROM.bin")
    # pyboy = PyBoy("SDL2", 1, rom, "ROMs/DMG_ROM.bin")
    pyboy.disableTitle()
    pyboy.setEmulationSpeed(False)
    serial_output = ""
    t = time.time()
    result = None
    while not pyboy.tick():
        b = pyboy.getSerial()
        if b != "":
            serial_output += b
            # print b,
            t = time.time()

        if "Passed" in serial_output:
            result = ("Passed")
            break
        elif "Failed" in serial_output:
            result = (serial_output)
            break

        if time.time() - t > timeout:
            result = ("Timeout:\n" + serial_output)
            break
    print(serial_output)
    pyboy.stop(save=False)
    return result
Пример #7
0
    def __init__(self, filename, rombanks, external_ram_count, carttype, sram, battery, rtc_enabled):
        self.filename = filename + ".ram"
        self.rombanks = rombanks
        self.carttype = carttype

        self.battery = battery
        self.rtc_enabled = rtc_enabled

        if self.rtc_enabled:
            self.rtc = RTC(filename)

        self.rambank_initialized = False
        self.external_rom_count = len(rombanks)
        self.external_ram_count = external_ram_count
        self.init_rambanks(external_ram_count)
        self.gamename = self.getgamename(rombanks)

        self.memorymodel = 0
        self.rambank_enabled = False
        self.rambank_selected = 0
        self.rombank_selected = 1

        self.is_cgb = self.getitem(0x0143) >> 7
  
        if not os.path.exists(self.filename):
            logger.info("No RAM file found. Skipping.")
        else:
            with open(self.filename, "rb") as f:
                self.load_ram(IntIOWrapper(f))
Пример #8
0
    def handle_events(self, events):
        global mark_counter, marked_tiles

        self.tilemap.refresh_lcdc()

        # Feed events into the loop
        events = BaseDebugWindow.handle_events(self, events)
        for event in events:
            if event == WindowEvent._INTERNAL_MOUSE and event.window_id == self.window_id:
                if event.mouse_button == 0:
                    tile_x, tile_y = event.mouse_x // self.scale // 8, event.mouse_y // self.scale // 8
                    tile_identifier = self.tilemap.tile_identifier(
                        tile_x, tile_y)
                    logger.info(f"Tile clicked on {tile_x}, {tile_y}")
                    marked_tiles.add(
                        MarkedTile(tile_identifier=tile_identifier,
                                   mark_id="TILE",
                                   mark_color=MARK[mark_counter]))
                    mark_counter += 1
                    mark_counter %= len(MARK)
                elif event.mouse_button == 1:
                    marked_tiles.clear()
            elif event == WindowEvent._INTERNAL_MARK_TILE:
                marked_tiles.add(event.tile_identifier)

        return events
Пример #9
0
    def save(self, path=None, fps=60):
        logger.info("ScreenRecorder saving...")

        if path is None:
            directory = os.path.join(os.path.curdir, "recordings")
            if not os.path.exists(directory):
                os.makedirs(directory, mode=0o755)
            path = os.path.join(
                directory,
                time.strftime(
                    f"{self.pyboy.cartridge_title()}-%Y.%m.%d-%H.%M.%S.gif"))

        if len(self.frames) > 0:
            self.frames[0].save(path,
                                save_all=True,
                                interlace=False,
                                loop=0,
                                optimize=True,
                                append_images=self.frames[1:],
                                duration=int(round(1000 / fps, -1)))

            logger.info("Screen recording saved in {}".format(path))
        else:
            logger.error("Screen recording failed: no frames")
        self.frames = []
Пример #10
0
def main():
    argv = parser.parse_args()
    log_level(argv.log_level)

    logger.info(
        """
The Game Boy controls are as follows:

| Keyboard key | GameBoy equivalant |
| ---          | ---                |
| Up           | Up                 |
| Down         | Down               |
| Left         | Left               |
| Right        | Right              |
| A            | A                  |
| S            | B                  |
| Return       | Start              |
| Backspace    | Select             |

The other controls for the emulator:

| Keyboard key | Emulator function       |
| ---          | ---                     |
| Escape       | Quit                    |
| D            | Debug                   |
| Space        | Unlimited FPS           |
| Z            | Save state              |
| X            | Load state              |
| I            | Toggle screen recording |
| O            | Save screenshot         |
| ,            | Rewind backwards        |
| .            | Rewind forward          |

See "pyboy --help" for how to enable rewind and other awesome features!
"""
    )

    # Start PyBoy and run loop
    pyboy = PyBoy(argv.ROM, **vars(argv))

    if argv.loadstate is not None:
        if argv.loadstate == INTERNAL_LOADSTATE:
            # Guess filepath from ROM path
            state_path = argv.ROM + ".state"
        else:
            # Use filepath given
            state_path = argv.loadstate

        valid_file_path(state_path)
        with open(state_path, "rb") as f:
            pyboy.load_state(f)

    while not pyboy.tick():
        pass

    pyboy.stop()

    if argv.profiling:
        print("\n".join(profiling_printer(pyboy._cpu_hitrate())))
Пример #11
0
 def _pause(self):
     if self.paused:
         return
     self.paused = True
     self.save_target_emulationspeed = self.target_emulationspeed
     self.target_emulationspeed = 1
     logger.info("Emulation paused!")
     self._update_window_title()
Пример #12
0
 def save_state(self, f):
     logger.info("Saving state...")
     f.write(self.bootrom_enabled.to_bytes(1, 'little'))
     self.cpu.save_state(f)
     self.lcd.save_state(f)
     self.ram.save_state(f)
     self.cartridge.save_state(f)
     logger.info("State saved.")
Пример #13
0
 def handle_events(self, events):
     for event in events:
         if event == WindowEvent.SCREEN_RECORDING_TOGGLE:
             self.recording ^= True
             if not self.recording:
                 self.save()
             else:
                 logger.info("ScreenRecorder started")
             break
     return events
Пример #14
0
    def save_ram(self, f):
        if not self.rambank_initialized:
            logger.info("Saving RAM is not supported on {}".format(
                self.carttype))
            return

        for bank in range(self.external_ram_count):
            for byte in range(8 * 1024):
                f.write(self.rambanks[bank][byte].to_bytes(1, "little"))

        logger.info("RAM saved.")
Пример #15
0
    def load_ram(self, f):
        if not self.rambank_initialized:
            logger.info("Loading RAM is not supported on {}".format(
                self.carttype))
            return

        for bank in range(self.external_ram_count):
            for byte in range(8 * 1024):
                self.rambanks[bank][byte] = ord(f.read(1))

        logger.info("RAM loaded.")
Пример #16
0
    def load_state(self, f):
        logger.info("Loading state...")
        self.bootrom_enabled = ord(f.read(1))
        self.cpu.load_state(f)
        self.lcd.load_state(f)
        self.ram.load_state(f)
        self.cartridge.load_state(f)
        logger.info("State loaded.")

        self.window.clearcache = True
        self.window.update_cache(self.lcd)
Пример #17
0
    def __init__(self, *args):
        super().__init__(*args)

        if not self.enabled():
            return

        if not self.pyboy_argv.get("loadstate"):
            logger.warning(
                "To replay input consistently later, it is recommended to load a state at boot. This will be"
                "embedded into the .replay file.")

        logger.info("Recording event inputs")
        self.recorded_input = []
Пример #18
0
    def save(self, path=None):

        if path is None:
            directory = os.path.join(os.path.curdir, "screenshots")
            if not os.path.exists(directory):
                os.makedirs(directory, mode=0o755)
            path = os.path.join(
                directory,
                time.strftime(
                    f"{self.pyboy.cartridge_title()}-%Y.%m.%d-%H.%M.%S.png"))

        self.pyboy.botsupport_manager().screen().screen_image().save(path)

        logger.info("Screenshot saved in {}".format(path))
Пример #19
0
    def __init__(self, gamerom_file, bootrom_file, window, profiling=False):
        if bootrom_file is not None:
            logger.info("Boot-ROM file provided")

        if profiling:
            logger.info("Profiling enabled")

        self.window = window
        self.timer = timer.Timer()
        self.interaction = interaction.Interaction()
        self.cartridge = cartridge.Cartridge(gamerom_file)
        self.bootrom = bootrom.BootROM(bootrom_file)
        self.ram = ram.RAM(random=False)
        self.cpu = cpu.CPU(self, profiling)
        self.lcd = lcd.LCD(window.color_palette)
        self.bootrom_enabled = True
        self.serialbuffer = u''
Пример #20
0
 def setitem(self, address, value):
     if 0x2000 <= address < 0x4000:
         if value == 0:
             value = 1
         self.rombank_selected = (value & 0b1)
         logger.info("Switching bank 0x%0.4x, 0x%0.2x" % (address, value))
     elif 0xA000 <= address < 0xC000:
         if self.rambanks is None:
             from . import EXTERNAL_RAM_TABLE
             logger.warning(
                 "Game tries to set value 0x%0.2x at RAM address 0x%0.4x, but "
                 "RAM banks are not initialized. Initializing %d RAM banks as "
                 "precaution" % (value, address, EXTERNAL_RAM_TABLE[0x02]))
             self.init_rambanks(EXTERNAL_RAM_TABLE[0x02])
         self.rambanks[self.rambank_selected][address - 0xA000] = value
     else:
         logger.warning("Unexpected write to 0x%0.4x, value: 0x%0.2x" %
                        (address, value))
Пример #21
0
    def getregister(self, register):
        if not self.latch_enabled:
            logger.info("RTC: Get register, but nothing is latched! 0x%0.2x" % register)

        if register == 0x08:
            return self.sec_latch
        elif register == 0x09:
            return self.min_latch
        elif register == 0x0A:
            return self.hour_latch
        elif register == 0x0B:
            return self.day_latch_low
        elif register == 0x0C:
            day_high = self.day_latch_high & 0b1
            halt = self.halt << 6
            day_carry = self.day_carry << 7
            return day_high + halt + day_carry
        else:
            logger.warning("Invalid RTC register: %0.4x" % (register))
Пример #22
0
    def __init__(self, filename):
        self.filename = filename + ".rtc"

        if not os.path.exists(self.filename):
            logger.info("No RTC file found. Skipping.")
        else:
            with open(self.filename, "rb") as f:
                self.load_state(IntIOWrapper(f))

        self.latch_enabled = False

        self.timezero = time.time()

        self.sec_latch = 0
        self.min_latch = 0
        self.hour_latch = 0
        self.day_latch_low = 0
        self.day_latch_high = 0
        self.day_carry = 0
        self.halt = 0
Пример #23
0
def Cartridge(filename):
    rombanks = load_romfile(filename)
    if not validate_checksum(rombanks):
        raise Exception("Cartridge header checksum mismatch!")

    # WARN: The following table doesn't work for MBC2! See Pan Docs
    external_ram_count = int(EXTERNAL_RAM_TABLE[rombanks[0][0x0149]])

    carttype = rombanks[0][0x0147]
    cartinfo = CARTRIDGE_TABLE.get(carttype, None)
    if carttype is None:
        raise Exception("Catridge type invalid: %s" % carttype)

    cartdata = (carttype, cartinfo[0].__name__, ", ".join(
        [x for x, y in zip(["SRAM", "Battery", "RTC"], cartinfo[1:]) if y]))
    logger.info("Cartridge type: 0x%0.2x - %s, %s" % cartdata)
    logger.info(
        "Cartridge size: %d ROM banks of 16KB, %s RAM banks of 8KB" %
        (len(rombanks), EXTERNAL_RAM_TABLE.get(external_ram_count, None)))
    cartmeta = CARTRIDGE_TABLE[carttype]

    return cartmeta[0](filename, rombanks, external_ram_count, carttype,
                       *cartmeta[1:])
Пример #24
0
    def stop(self, save=True):
        """
        Gently stops the emulator and all sub-modules.

        Args:
            save (bool): Specify whether to save the game upon stopping. It will always be saved in a file next to the
                provided game-ROM.
        """
        logger.info("###########################")
        logger.info("# Emulator is turning off #")
        logger.info("###########################")
        self.plugin_manager.stop()
        self.mb.stop(save)
Пример #25
0
 def __init__(self,
              event=WindowEvent._INTERNAL_MARK_TILE,
              tile_identifier=-1,
              mark_id="",
              mark_color=0,
              sprite_height=8,
              sprite=False):
     self.tile_identifier = tile_identifier
     self.mark_id = mark_id
     self.mark_color = mark_color
     self.sprite_height = sprite_height
     if mark_id == "TILE":
         # TODO: Use __str__ of the Tile and Sprite classes
         logger.info(f"Marked Tile - identifier: {tile_identifier}")
     elif mark_id == "SPRITE":
         logger.info(
             f"Marked Sprite - tile identifier: {tile_identifier}, sprite height: {sprite_height}"
         )
     else:
         logger.info(
             f"Marked {mark_id} - tile identifier: {tile_identifier}")
Пример #26
0
 def load_state(self, f):
     self.timezero = struct.unpack('f', f.read(4))[0]
     self.halt = ord(f.read(1))
     self.day_carry = ord(f.read(1))
     logger.info("RTC loaded.")
Пример #27
0
 def set_title(self, title):
     logger.info(title.encode())
Пример #28
0
 def save_state(self, f):
     for b in struct.pack('f', self.timezero):
         f.write(b)
     f.write(self.halt)
     f.write(self.day_carry)
     logger.info("RTC saved.")
Пример #29
0
 def load_state(self, f):
     self.timezero = struct.unpack('f',
                                   bytes([f.read() for _ in range(4)]))[0]
     self.halt = f.read()
     self.day_carry = f.read()
     logger.info("RTC loaded.")
Пример #30
0
 def set_title(self, title):
     logger.info(title)