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))
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))
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()
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
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)
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
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))
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
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 = []
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())))
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()
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.")
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
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.")
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.")
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)
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 = []
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))
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''
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))
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))
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
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:])
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)
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}")
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.")
def set_title(self, title): logger.info(title.encode())
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.")
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.")
def set_title(self, title): logger.info(title)