def enabled(self): if Image is None: logger.warning( f"{__name__}: Missing dependency \"Pillow\". Screenshots disabled" ) return False return True
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 setitem(self, address, value): if 0x0000 <= address < 0x2000: self.rambank_enabled = (value & 0b00001111) == 0b1010 elif 0x2000 <= address < 0x4000: value &= 0b00011111 # The register cannot contain zero (0b00000) and will be initialized as 0b00001 # Attempting to write 0b00000 will write 0b00001 instead. if value == 0: value = 1 self.bank_select_register1 = value elif 0x4000 <= address < 0x6000: self.bank_select_register2 = value & 0b11 elif 0x6000 <= address < 0x8000: self.memorymodel = value & 0b1 elif 0xA000 <= address < 0xC000: if self.rambanks is None: 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, self.external_ram_count)) self.init_rambanks(self.external_ram_count) if self.rambank_enabled: self.rambank_selected = self.bank_select_register2 if self.memorymodel == 1 else 0 self.rambanks[self.rambank_selected % self.external_ram_count][address - 0xA000] = value else: logger.error("Invalid writing address: %s" % hex(address))
def setitem(self, address, value): if 0x0000 <= address < 0x2000: # 8-bit register. All bits matter, so only 0b00001010 enables RAM. self.rambank_enabled = (value == 0b00001010) elif 0x2000 <= address < 0x3000: # 8-bit register used for the lower 8 bits of the ROM bank number. self.rombank_selected = (self.rombank_selected & 0b100000000) | value elif 0x3000 <= address < 0x4000: # 1-bit register used for the most significant bit of the ROM bank number. self.rombank_selected = ( (value & 0x1) << 8) | (self.rombank_selected & 0xFF) elif 0x4000 <= address < 0x6000: self.rambank_selected = value & 0xF elif 0xA000 <= address < 0xC000: if self.rambanks is None: 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, self.external_ram_count)) self.init_rambanks(self.external_ram_count) if self.rambank_enabled: self.rambanks[self.rambank_selected % self.external_ram_count][address - 0xA000] = value else: logger.error("Unexpected write to 0x%0.4x, value: 0x%0.2x" % (address, value))
def writecommand(self, value): if value == 0x00: self.latch_enabled = False elif value == 0x01: if not self.latch_enabled: self.latch_rtc() self.latch_enabled = True else: logger.warning("Invalid RTC command: %0.2x" % value)
def load_ram(self, f): if not self.rambank_initialized: logger.warning("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] = f.read() logger.debug("RAM loaded.")
def save_ram(self, f): if not self.rambank_initialized: logger.warning("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]) logger.debug("RAM saved.")
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 set_emulation_speed(self, target_speed): """ Set the target emulation speed. It might loose accuracy of keeping the exact speed, when using a high `target_speed`. The speed is defined as a multiple of real-time. I.e `target_speed=2` is double speed. A `target_speed` of `0` means unlimited. I.e. fastest possible execution. Args: target_speed (int): Target emulation speed as multiplier of real-time. """ if target_speed > 5: logger.warning("The emulation speed might not be accurate when speed-target is higher than 5") self.target_emulationspeed = target_speed
def main(): argv = parser.parse_args() if argv.no_logger: logger.disabled = True else: addconsolehandler() if argv.record_input and not argv.loadstate: logger.warning( "To replay input consistently later, it is required to load a state at boot. This will be" "embedded into the .replay file.") # Start PyBoy and run loop pyboy = PyBoy( argv.ROM, window_type=argv.window, window_scale=argv.scale, bootrom_file=argv.bootrom, autopause=argv.autopause, debugging=argv.debug, profiling=argv.profiling, record_input=argv.record_input is not None, disable_input=argv.no_input, enable_rewind=argv.rewind, ) if argv.loadstate is not None: if argv.loadstate != '': # Use filepath given with open(argv.loadstate, 'rb') as f: pyboy.load_state(f) else: # Guess filepath from ROM path with open(argv.ROM + ".state", 'rb') as f: pyboy.load_state(f) while not pyboy.tick(): pass pyboy.stop() if argv.profiling: print("\n".join(profiling_printer(pyboy._get_cpu_hitrate()))) if argv.record_input: save_replay(argv.ROM, argv.loadstate, argv.record_input, pyboy._get_recorded_input())
def setitem(self, address, value): if 0x2000 <= address < 0x4000: if value == 0: value = 1 self.rombank_selected = (value & 0b1) logger.debug("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 set_lives_left(self, amount): """ Set the amount lives to any number between 0 and 99. This should only be called when the game has started. Args: amount (int): The wanted number of lives """ if not self.game_has_started: logger.warning("Please call set_lives_left after starting the game") if 0 <= amount <= 99: tens = amount // 10 ones = amount % 10 self.pyboy.set_memory_value(ADDR_LIVES_LEFT, (tens << 4) | ones) self.pyboy.set_memory_value(ADDR_LIVES_LEFT_DISPLAY, tens) self.pyboy.set_memory_value(ADDR_LIVES_LEFT_DISPLAY + 1, ones) else: logger.error(f"{amount} is out of bounds. Only values between 0 and 99 allowed.")
def setitem(self, address, value): if 0x0000 <= address < 0x2000: if (value & 0b00001111) == 0b1010: self.rambank_enabled = True elif value == 0: self.rambank_enabled = False else: # Pan Docs: "Practically any value with 0Ah in the # lower 4 bits enables RAM, and any other value # disables RAM." self.rambank_enabled = False logger.warning( "Unexpected command for MBC3: Address: 0x%0.4x, Value: 0x%0.2x" % (address, value)) elif 0x2000 <= address < 0x4000: if value == 0: value = 1 # print "ROM Bank switch:", value & 0b01111111 self.rombank_selected = value & 0b01111111 # sets 7LSB of ROM bank address elif 0x4000 <= address < 0x6000: self.rambank_selected = value elif 0x6000 <= address < 0x8000: if self.rtc_enabled: self.rtc.writecommand(value) else: # NOTE: Pokemon Red/Blue will do this, but it can safely be ignored: # https://github.com/pret/pokered/issues/155 logger.warning( "RTC not present. Game tried to issue RTC command: 0x%0.4x, 0x%0.2x" % (address, value)) elif 0xA000 <= address < 0xC000: if self.rambank_selected <= 0x03: self.rambanks[self.rambank_selected][address - 0xA000] = value elif 0x08 <= self.rambank_selected <= 0x0C: self.rtc.setregister(self.rambank_selected, value) else: raise logger.error("Invalid RAM bank selected: 0x%0.2x" % self.rambank_selected) else: raise logger.error("Invalid writing address: 0x%0.4x" % address)
def setitem(self, address, value): if 0x0000 <= address < 0x2000: if (value & 0b00001111) == 0x0A: self.rambank_enabled = True else: self.rambank_enabled = False elif 0x2000 <= address < 0x4000: # But (when using the register below to specify the upper ROM Bank bits), the same # happens for Bank 20h, 40h, and 60h. Any attempt to address these ROM Banks will # select Bank 21h, 41h, and 61h instead. if value == 0: value = 1 # sets 5LSB of ROM bank address self.rombank_selected = self.rombank_selected & 0b11100000 | value & 0b00011111 elif 0x4000 <= address < 0x6000: # Note: 16Mbit = 2MB. 2MB/(8KB banks) = 128 banks. 128 is addressable with 7 bits if self.memorymodel == 0: # 16/8 mode # sets 2MSB of ROM bank address self.rombank_selected = self.rombank_selected & 0b00011111 | ( address & 0b11) << 5 # Note: 4Mbit = 0.5MB. 0.5MB/(8KB banks) = 32 banks. 32 is addressably with 5 bits elif self.memorymodel == 1: # 4/32 mode self.rambank_selected = value & 0b00000011 else: raise logger.error("Invalid memory model: %s" % self.memorymodel) elif 0x6000 <= address < 0x8000: self.memorymodel = value & 0x1 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: raise logger.error("Invalid writing address: %s" % hex(address))
assert argv_load_state_file[ 0] != '-', "Load state file looks like an argument" argv_record_input_file = None argv_record_input = "--record-input" in sys.argv if argv_record_input: assert not argv_disable_input idx = sys.argv.index("--record-input") assert len(sys.argv) > idx + 1 argv_record_input_file = sys.argv[idx + 1] assert argv_record_input_file[ 0] != '-', "Output file looks like an argument" if argv_record_input and not argv_loadstate: logger.warning( "To replay input consistently later, it is required to load a state at boot. This will be embedded" "into the .replay file.") def getROM(romdir): """Give a list of ROMs to start""" found_files = list( filter( lambda f: f.lower().endswith(".gb") or f.lower().endswith(".gbc"), os.listdir(romdir))) for i, f in enumerate(found_files): print("%s\t%s" % (i + 1, f)) filename = input("Write the name or number of the ROM file:\n") try: filename = romdir + found_files[int(filename) - 1]