Memory view¶
Technical overview. The Game Boy exposes a 16-bit address space, but parts of that space are shared with banked ROM, cartridge RAM, video RAM, and working RAM. Hardware registers also live in the address space and can have side effects when read or written.
- class pyboy.PyBoyMemoryView(mb)[source]¶
Bases:
objectThis class cannot be used directly, but is accessed through
PyBoy.memory.This class serves four purposes: Reading memory (ROM/RAM), writing memory (RAM), overriding memory (ROM) and special registers.
See the Pan Docs: Memory Map for a great overview of the memory space.
Memory can be accessed as individual bytes (
pyboy.memory[0x00]) or as slices (pyboy.memory[0x00:0x10]). And if applicable, a specific ROM/RAM bank can be defined before the address (pyboy.memory[0, 0x00]orpyboy.memory[0, 0x00:0x10]).The boot ROM is accessed using the special
-1ROM bank.The find addresses of interest, either search online for something like: “[game title] RAM map”, or find them yourself using
PyBoy.memory_scanner.Read:
If you’re developing a bot or AI with this API, you’re most likely going to be using read the most. This is how you would efficiently read the score, time, coins, positions etc. in a game’s memory.
At this point, all reads will return a new list of the values in the given range. The slices will not reference back to the PyBoy memory. This feature might come in the future.
>>> pyboy.memory[0x0000] # Read one byte at address 0x0000 49 >>> pyboy.memory[0x0000:0x0010] # Read 16 bytes from 0x0000 to 0x0010 (excluding 0x0010) [49, 254, 255, 33, 0, 128, 175, 34, 124, 254, 160, 32, 249, 6, 48, 33] >>> pyboy.memory[-1, 0x0000:0x0010] # Read 16 bytes from 0x0000 to 0x0010 (excluding 0x0010) from the boot ROM [49, 254, 255, 33, 0, 128, 175, 34, 124, 254, 160, 32, 249, 6, 48, 33] >>> pyboy.memory[0, 0x0000:0x0010] # Read 16 bytes from 0x0000 to 0x0010 (excluding 0x0010) from ROM bank 0 [64, 65, 66, 67, 68, 69, 70, 65, 65, 65, 71, 65, 65, 65, 72, 73] >>> pyboy.memory[2, 0xA000] # Read from external RAM on cartridge (if any) from bank 2 at address 0xA000 0
Write:
Writing to Game Boy memory can be complicated because of the limited address space. There’s a lot of memory that isn’t directly accessible, and can be hidden through “memory banking”. This means that the same address range (for example 0x4000 to 0x8000) can change depending on what state the game is in.
If you want to change an address in the ROM, then look at override below. Issuing writes to the ROM area actually sends commands to the Memory Bank Controller (MBC) on the cartridge.
A write is done by assigning to the
PyBoy.memoryobject. It’s recommended to define the bank to avoid mistakes (pyboy.memory[2, 0xA000]=1). Without defining the bank, PyBoy will pick the current bank for the given address if needed (pyboy.memory[0xA000]=1).>>> pyboy.memory[0xC000] = 123 # Write to WRAM at address 0xC000 >>> pyboy.memory[0xC000:0xC00A] = [0,1,2,3,4,5,6,7,8,9] # Write to WRAM from address 0xC000 to 0xC00A >>> pyboy.memory[0xC010:0xC01A] = 0 # Write to WRAM from address 0xC010 to 0xC01A >>> pyboy.memory[0x1000] = 123 # Not writing 123 at address 0x1000! This sends a command to the cartridge's MBC. >>> pyboy.memory[2, 0xA000] = 123 # Write to external RAM on cartridge (if any) for bank 2 at address 0xA000 >>> # Game Boy Color (CGB) only: >>> pyboy_cgb.memory[1, 0x8000] = 25 # Write to VRAM bank 1 at address 0x8000 when in CGB mode >>> pyboy_cgb.memory[6, 0xD000] = 25 # Write to WRAM bank 6 at address 0xD000 when in CGB mode
Override:
Override data at a given memory address of the Game Boy’s ROM.
This can be used to reprogram a game ROM to change its behavior.
This will not let you override RAM or a special register. This will let you override data in the ROM at any given bank. This is the memory allocated at 0x0000 to 0x8000, where 0x4000 to 0x8000 can be changed from the MBC.
_NOTE_: Any changes here are not saved or loaded to game states! Use this function with caution and reapply any overrides when reloading the ROM.
To override, it’s required to provide the ROM-bank you’re changing. Otherwise, it’ll be considered a regular ‘write’ as described above.
>>> pyboy.memory[0, 0x0010] = 10 # Override ROM-bank 0 at address 0x0010 >>> pyboy.memory[0, 0x0010:0x001A] = [0,1,2,3,4,5,6,7,8,9] # Override ROM-bank 0 at address 0x0010 to 0x001A >>> pyboy.memory[-1, 0x0010] = 10 # Override boot ROM at address 0x0010 >>> pyboy.memory[1, 0x6000] = 12 # Override ROM-bank 1 at address 0x6000 >>> pyboy.memory[0x1000] = 12 # This will not override, as there is no ROM bank assigned!
Special Registers:
The Game Boy has a range of memory addresses known as hardware registers. These control parts of the hardware like LCD, Timer, DMA, serial and so on. Even though they might appear as regular RAM addresses, reading/writing these addresses often results in special side-effects.
The DIV (0xFF04) register for example provides a number that increments 16 thousand times each second. This can be used as a source of randomness in games. If you read the value, you’ll get a pseudo-random number. But if you write any value to the register, it’ll reset to zero.
>>> pyboy.memory[0xFF04] # DIV register 231 >>> pyboy.memory[0xFF04] = 123 # Trying to write to it will always reset it to zero >>> pyboy.memory[0xFF04] 0