class Bus(object): def __init__(self): self.cpu = CPU() self.cpu.set_bus(self) self.ppu = PPU() self.cpu_ram = [0] * 64 * 1024 def cpu_read(self, address, read_only): ignore_next, data = self.cartridge.cpu_read(address) if ignore_next: pass elif 0x0000 <= address <= 0x1FFF: # System RAM Address Range, mirrored every 2048 data = self.cpu_ram[address & 0x07FF] elif 0x2000 <= address <= 0x3FFF: # PPU Address range, mirrored every 8 data = self.ppu.cpu_read(address & 0x0007, read_only) return data def cpu_write(self, address, data): if self.cartridge.cpu_write(address, data): pass if 0x0000 <= address <= 0xffff: self.cpu_ram[address] = data def set_cartridge(self, cartridge): self.cartridge = cartridge self.ppu.set_cartridge(cartridge)
def __init__(self): self.cpu = CPU(self) self.mmu = MMU(self) self.ppu = PPU(self) self.cartridge = None self.ram = [0xFF] * 2048 self._clock = 0
def __init__(self): self.cpu = CPU() self.cpu.set_bus(self) self.ppu = PPU() self.cpu_ram = [0] * 64 * 1024
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
def __init__(self): self.MEMORY_SIZE = 0x800 # 2kB self.rom = None self.ram = RAM(self.MEMORY_SIZE) self.cpu = CPU(self.cpu_read, self.cpu_write) self.ppu = PPU(self.ppu_read, self.ppu_write) self.system_clock = 0
def __init__(self, RAM, ROM, SRAM, use_MAD1_mapping, SRAM_size): self.RAM = RAM # TODO: maybe rename to WRAM self.ROM = ROM self.SRAM = SRAM self.use_MAD1_mapping = use_MAD1_mapping self.SRAM_size = SRAM_size self.dma = DMAController() self.hdm = HDMAController() self.internal_cpu_registers = InternalCPURegisters() self.ppu = PPU()
def __init__(self, file_name): pygame.init() self.screen = pygame.display.set_mode([256, 240]) self.nes_file = self.read_nes_file(file_name) self.main_memory = [0 for _ in range(0x800)] self.save_memory = [0 for _ in range(0x2000)] self.ppu = PPU(self.nes_file) self.cpu = CPU(self.nes_file, self.main_memory, self.save_memory, self.ppu)
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)
def main(): # Set up command line arguments parser parser = argparse.ArgumentParser(description='NES Emulator') parser.add_argument('rom_path', metavar='R', type=str, help='The location to the rom file') args = parser.parse_args() # load rom with open(args.rom_path, 'rb') as file: rom_bytes = file.read() rom = ROM(rom_bytes) # create ram ram = RAM() # create ppu ppu = PPU() # create cpu cpu = CPU(ram, ppu) cpu.start_up() cpu.run_rom(rom)
def __init__(self, rom_path): self.rom_path = rom_path self._screen = Screen() self._memory = Memory() self._ppu = PPU(self._screen, self._memory) self._apu = APU(self._memory) self._rom = ROM(rom_path) self._cpu = CPU(self._memory, self._ppu, self._apu, self._rom)
def __init__(self, rom: str, skipBios: bool = False, nostalgic: bool = False) -> None: # main units self.z80 = Z80(self) self.ppu = PPU(self, nostalgic) self.mmu = MMU(self, rom) self.timer = Timer(self) # log dict self.LOG = {} # global states self.frames = 0 self.paused = False self.initData(skipBios) print(INSTRUCTION)
def cpu(): ram = RAM() ppu = PPU() cpu = CPU(ram, ppu) cpu.start_up() cpu.rom = MagicMock() cpu.rom.memory_start_location = 0 cpu.rom.memory_end_location = 0x1FFF return cpu
def main(): mode = 1 frequency = 0 if len(sys.argv) >= 2: cpu = CPU(mode=0 if "async" in sys.argv else 1, frequency=frequency, console=False if "noconsole" in sys.argv else True) else: cpu = CPU(mode=mode, frequency=frequency, console=True) if sys.platform == "win32": ppu = PPU(cpu) ppu.start() clear() print(cpu) print("6502 Emulator by Andrija Jovanovic\n") print("Running ROM: ", end='') print(cpu.rom_path) input("\nPress <Enter> to start...") cpu.start()
class NES: def __init__(self): self.cpu = CPU(self) self.mmu = MMU(self) self.ppu = PPU(self) self.cartridge = None self.ram = [0xFF] * 2048 self._clock = 0 def reset(self): self.cpu.reset() def start(self): if (self.cartridge is None): raise RuntimeError("No ROM loaded!") self.cpu.reset() # TODO: Create proper execution loop. while True: self.frame() #time.sleep(0.016) def frame(self): self.step() def step(self): self.cpu.step() def consume_cycles(self, cycles): # Update clock with instruction cycles. self._clock = (self._clock + cycles) & 0xFFFFFFFF self.ppu.step(cycles) def load_cartridge(self, filename): self.cartridge = Cartridge(filename)
def __init__(self): self.cpu = CPU(self) self.ppu = PPU(self) self.papu = PAPU(self) self.keyboard = Keyboard(self) self.rom = None self.romFile = None self.mmap = None self.isRunning = False self.limitFrames = True self.fpsFrameCount = 0 self.opts = Nes.Options() self.frameTime = self.opts.preferredFrameRate
class NES(): def __init__(self, prg, chr): self.ppu = PPU(chr) self.cpu = CPU(prg, self.ppu) self.frame = self.ppu.init_frame() def cycle(self): start = time.time() ########## out = None while not out: cycle = self.cpu.run() out = self.ppu.run(cycle * 3) self.frame = out ########### #time.sleep(max(0,(1.0/60-(time.time() - start)))) #time.sleep(0.01) return def run(self): while True: self.cycle()
def main(): # set up command line argument parser parser = argparse.ArgumentParser(description="NES Emulator.") parser.add_argument('rom_path', metavar='R', type=str, help='path to nes rom') parser.add_argument('--test') args = parser.parse_args() # TODO: validate rom path is correct print(args.rom_path) # load rom with open(args.rom_path, 'rb') as file: rom_bytes = file.read() # create ram ram = RAM() # create ppu ppu = PPU() # create apu apu = APU() rom = ROM(rom_bytes) # create cpu cpu: CPU = CPU(ram, ppu, apu) cpu.start_up() cpu.load_rom(rom, args.test) # check if running test rom if args.test: # load in the test log with open('test_log.log', 'r') as nes_test_file: nes_test_log = NesTestLog(nes_test_file.readlines()) while True: cpu.identify() nes_test_log.compare(cpu) cpu.execute() else: while True: cpu.identify() cpu.execute()
def __init__(self, rom_bytes, testing): self.rom = ROM(rom_bytes) # create ram self.ram = RAM() # create ppu and apu self.ppu = PPU() self.apu = APU() # create cpu self.cpu = CPU(self.ram, self.ppu, self.apu) # create ppu window self.window = Window() self.testing = testing self.nes_test_log = None
def main(): # Check if there's a parameter if len(sys.argv) != 2: print ("Provide a .nes file\n") return window = Window() window.set_size(256, 240) # window.set_size(768, 720) cpu = CPU() ppu = PPU(window) bus = BUS(cpu, ppu) cart = Cartridge(sys.argv[1]) bus.insertCartridge(cart) cpu.connectBus(bus) # Do all init (load pallettes, background, set flags...) before running pyglet loop pyglet.clock.schedule_interval(bus.setFrame, 1/60.0) pyglet.app.run()
def main(): parser = argparse.ArgumentParser(description='NesEmulator :)') parser.add_argument('rom_path', metavar='R', type=str, help='Path to the NES ROM') args = parser.parse_args() # TODO: Implement drag and drop rom system print(args.rom_path) with open(args.rom_path, 'rb') as file: rom_bytes = file.read() rom = ROM(rom_bytes) ram = RAM() ppu = PPU() cpu = CPU(ram, ppu) cpu.start_up() cpu.run_rom(rom)
def main(): parser = argparse.ArgumentParser(description='NES Emulator.') parser.add_argument('rom_path', metavar='R', type=str, help='path to the nes rom') args = parser.parse_args() print(args.rom_path) with open(args.rom_path, 'rb') as file: rom_bytes = file.read() rom = ROM(rom_bytes) memory = Memory() ppu = PPU() cpu = CPU(memory, ppu) cpu.initialization() cpu.load_rom(rom)
class Nes: #class ExecutionThread(Thread): # def __init__(self, nes): # self.nes = nes # Thread.__init__(self) # # def run(self): # print "Execution thread started." # while self.nes.isRunning: # self.nes.cpu.emulate() class Options: CPU_FREQ_NTSC= 1789772.5 #1789772.72727272d CPU_FREQ_PAL= 1773447.4 def __init__(self): self.preferredFrameRate= 60, self.fpsInterval= 500 # Time between updating FPS in ms self.showDisplay= True self.emulateSound= False self.sampleRate= 44100 # Sound sample rate in hz def __init__(self): self.cpu = CPU(self) self.ppu = PPU(self) self.papu = PAPU(self) self.keyboard = Keyboard(self) self.rom = None self.romFile = None self.mmap = None self.isRunning = False self.limitFrames = True self.fpsFrameCount = 0 self.opts = Nes.Options() self.frameTime = self.opts.preferredFrameRate #self.executionThread = Nes.ExecutionThread(self) def reset(self): if self.mmap: self.mmap.reset() self.cpu.reset() self.ppu.reset() self.papu.reset() def start(self): if (self.rom and self.rom.valid): if (not self.isRunning): self.isRunning = True; self.ppu.startFrame() #self.executionThread.start() else: print "There is no ROM loaded, or it is invalid." def printFps(self): pass def stop(self): self.isRunning = False def reloadRom(self): if self.romFile: self.loadRom(self.romFile) def loadRom(self, file): if self.isRunning: self.stop() print "Loading rom {0}...".format(file) # Load ROM file self.rom = Rom(self) self.rom.load(file) if self.rom.valid: print "Rom loaded." self.reset() print "Creating rom mapper..." self.mmap = self.rom.createMapper() if not self.mmap: return self.mmap.loadROM() # TODO: self.ppu.setMirroring(self.rom.getMirroringType()) self.romFile = file print "Initialized NES, ready to start." else: print "Invalid ROM: {0}".format(file) return self.rom.valid
class Emulator(object): def __init__(self, file_name): pygame.init() self.screen = pygame.display.set_mode([256, 240]) self.nes_file = self.read_nes_file(file_name) self.main_memory = [0 for _ in range(0x800)] self.save_memory = [0 for _ in range(0x2000)] self.ppu = PPU(self.nes_file) self.cpu = CPU(self.nes_file, self.main_memory, self.save_memory, self.ppu) def read_nes_file(self, file_name): with open(file_name, 'rb') as f: header = Header(f.read(16)) trainer = f.read(512) if header.has_trainer else 0 nes_prg_rom = f.read(header.prg_rom_size) nes_chr_rom = f.read(header.chr_rom_size) return NesFile(header, trainer, nes_prg_rom, nes_chr_rom) def run(self): for event in pygame.event.get(): if event.type == QUIT: exit() elif event.type == pygame.KEYDOWN or event.type == pygame.KEYUP: if event.key == pygame.K_u: self.cpu.input_status[0] = event.type == pygame.KEYDOWN if event.key == pygame.K_i: self.cpu.input_status[1] = event.type == pygame.KEYDOWN if event.key == pygame.K_j: self.cpu.input_status[2] = event.type == pygame.KEYDOWN if event.key == pygame.K_k: self.cpu.input_status[3] = event.type == pygame.KEYDOWN if event.key == pygame.K_w: self.cpu.input_status[4] = event.type == pygame.KEYDOWN if event.key == pygame.K_s: self.cpu.input_status[5] = event.type == pygame.KEYDOWN if event.key == pygame.K_a: self.cpu.input_status[6] = event.type == pygame.KEYDOWN if event.key == pygame.K_d: self.cpu.input_status[7] = event.type == pygame.KEYDOWN cycles_per_scanline = 341 / 3 end_of_render = self.cpu.cpu_cycle + (240 + 1) * cycles_per_scanline end_of_vblank = end_of_render + 20 * cycles_per_scanline end_of_flame = self.cpu.cpu_cycle + 29780.5 is_8x16_mode = self.ppu.ppu_ctrl.bit5 sprites_number_per_lines = [0 for _ in range(256 + 16)] for index in range(64): for y in range(16 if is_8x16_mode else 8): # print(self.ppu.oam[index * 4], y) sprites_number_per_lines[self.ppu.oam[index * 4] + y] += 1 # ppu render flame for line in range(241): end_of_scanline = self.cpu.cpu_cycle + cycles_per_scanline # ppu render one scanline while self.cpu.cpu_cycle < end_of_scanline: self.cpu.exec_op() # hblank if line == self.ppu.oam[0]: self.ppu.ppu_status.bit6 = 1 if sprites_number_per_lines[line] > 8: self.ppu.ppu_status.bit5 = 1 while self.cpu.cpu_cycle < end_of_render: self.cpu.exec_op() # vblank self.ppu.ppu_status.value |= 0x80 self.cpu.nmi() while self.cpu.cpu_cycle < end_of_vblank: self.cpu.exec_op() while self.cpu.cpu_cycle < end_of_flame: self.cpu.exec_op() # end of flame, render and show if self.ppu.ppu_mask.bit3: self.ppu.render_background_1() if self.ppu.ppu_mask.bit4: self.ppu.render_sprites() pygame.surfarray.blit_array(self.screen, self.ppu.pixels) pygame.display.update() self.ppu.ppu_status.bit6 = 0 self.ppu.ppu_status.bit5 = 0
else: rom_file_name = 'supermario.nes' nes = NES() rom = ROM(nes) if rom.headercheck(rom_file_name) != True: exit() nes.rom = rom mem = MEMORY(nes) rom.load(rom_file_name, mem) nes.mem = mem cpu = CPU(nes) nes.cpu = cpu cpu.reset() ppu = PPU(nes) nes.ppu = ppu ppu.reset() disp = DISPLAY(nes) nes.disp = disp disp.init() in_put = INPUT(nes) nes.in_put = in_put in_put.reset() nes.start()
class Emulator(object): __slots__ = ('z80', 'ppu', 'mmu', 'timer', 'LOG', 'frames', 'paused') def __init__(self, rom: str, skipBios: bool = False, nostalgic: bool = False) -> None: # main units self.z80 = Z80(self) self.ppu = PPU(self, nostalgic) self.mmu = MMU(self, rom) self.timer = Timer(self) # log dict self.LOG = {} # global states self.frames = 0 self.paused = False self.initData(skipBios) print(INSTRUCTION) def step(self) -> None: if self.mmu.dmaInProgress: cyclesPassed = self.mmu.step() else: cyclesPassed = self.z80.step() self.takeLog() self.timer.tick(cyclesPassed) self.ppu.tick(cyclesPassed) def run(self, frames: int = -1) -> None: while self.frames != frames: self.step() self.ppu.handleEvents() def read(self, addr: int) -> int: return self.mmu.read(addr) def write(self, addr: int, byte: int) -> None: self.mmu.write(addr, byte) def initData(self, skipBios: bool) -> None: if not skipBios: return self.z80.PC = 0x100 self.z80.SP = 0xFFFE self.z80.A = 0x01 self.z80.B = 0x00 self.z80.C = 0x13 self.z80.D = 0x00 self.z80.E = 0xD8 self.z80.F = 0xB0 self.z80.H = 0x01 self.z80.L = 0x4D self.write(0xFF10, 0x80) self.write(0xFF11, 0xBF) self.write(0xFF12, 0xF3) self.write(0xFF14, 0xBF) self.write(0xFF16, 0x3F) self.write(0xFF19, 0xBF) self.write(0xFF1A, 0x7F) self.write(0xFF1B, 0xFF) self.write(0xFF1C, 0x9F) self.write(0xFF1E, 0xBF) self.write(0xFF20, 0xFF) self.write(0xFF23, 0xBF) self.write(0xFF24, 0x77) self.write(0xFF25, 0xF3) self.write(0xFF26, 0xF1) self.write(0xFF40, 0x91) self.write(0xFF41, 0x05) self.write(0xFF47, 0xFC) self.write(0xFF48, 0xFF) self.write(0xFF49, 0xFF) self.write(0xFF50, 0x01) def takeLog(self) -> None: self.LOG['frames'] = self.frames self.LOG['opcode'] = toHex(self.z80.opcode) self.LOG['opname'] = self.z80.opname self.LOG['arg0'] = toHex(self.z80.args[0]) self.LOG['arg1'] = toHex(self.z80.args[1]) self.LOG['PC'] = toHex(self.z80.PC, 4) self.LOG['SP'] = toHex(self.z80.SP, 4) self.LOG['A'] = toHex(self.z80.A) self.LOG['B'] = toHex(self.z80.B) self.LOG['C'] = toHex(self.z80.C) self.LOG['D'] = toHex(self.z80.D) self.LOG['E'] = toHex(self.z80.E) self.LOG['F'] = toHex(self.z80.F) self.LOG['H'] = toHex(self.z80.H) self.LOG['L'] = toHex(self.z80.L) def printReg(self) -> None: print('\nregisters:') for key, val in self.LOG.items(): print('\t', key, val) def printMem(self, title: str, start: int, stop: int, step: int) -> None: print(title) for addr in range(start, stop, step): print(f'\t{toHex(addr, length=4)}', end=' ') for offset in range(step): print(toHex(self.read(addr + offset)), end=' ') print()
def __init__(self, prg, chr): self.ppu = PPU(chr) self.cpu = CPU(prg, self.ppu) self.frame = self.ppu.init_frame()
class Emulator: def __init__(self): self.MEMORY_SIZE = 0x800 # 2kB self.rom = None self.ram = RAM(self.MEMORY_SIZE) self.cpu = CPU(self.cpu_read, self.cpu_write) self.ppu = PPU(self.ppu_read, self.ppu_write) self.system_clock = 0 def set_rom(self, rom: ROM): self.rom = rom self.cpu.reset() def has_rom(self): return self.rom is not None def tick_clock(self): self.ppu.clock() if self.system_clock % 3 == 0: self.cpu.clock() self.system_clock += 1 # ----------------------------------- PPU BUS ADDRESSING - 16 bits range - 0x0000 to 0xFFFF def ppu_write(self, addr, value): addr &= 0x3FFF # pattern memory # if addr <= 0x1FFF: # return self.rom.get_chr_data(addr) # self.ppu.tbl_pattern[int(addr >= 0x1000)][addr & 0x0FFF] # elif addr <= 0x3EFF: # pass # palette memory if 0x3F00 <= addr <= 0x3FFF: addr &= 0x001F # mirroring if addr == 0x0010 or addr == 0x0014 or addr == 0x0018 or addr == 0x001C: addr -= 0x10 self.ppu.tbl_palette[addr] = value return value def ppu_read(self, addr, byte=1): addr &= 0x3FFF # pattern memory if addr <= 0x1FFF: return self.rom.get_chr_data( addr ) # self.ppu.tbl_pattern[int(addr >= 0x1000)][addr & 0x0FFF] elif addr <= 0x3EFF: pass # palette memory elif 0x3F00 <= addr <= 0x3FFF: addr &= 0x001F # mirroring if addr == 0x0010 or addr == 0x0014 or addr == 0x0018 or addr == 0x001C: addr -= 0x10 return self.ppu.tbl_palette[addr] def __ppu_memory_access(self, write, addr, value, word=0): pass # ----------------------------------- CPU BUS ADDRESSING - 16 bits range - 0x0000 to 0xFFFF # CPU write to memory def cpu_write(self, addr, value): return self.__cpu_memory_access(True, addr, value) # CPU read from memory def cpu_read(self, addr, byte=1): if byte == 2: return self.__cpu_memory_access(False, addr, None, 1) return self.__cpu_memory_access(False, addr, None) def __cpu_memory_access(self, write, addr, value, word=0): # RAM ranges from 0x0000 to 0x2000 and uses mirroring each 0x800 if addr <= 0x1FFF: if write: return self.ram.cpu_write(addr, value) else: if word: return self.ram.get_word(addr) # pop 2 bytes from memory else: return self.ram.cpu_read(addr) # pop 1 byte from memory # PPU Ranges from 0x2000 to 0x3FFF elif addr <= 0x3FFF: if write: return self.ppu.cpu_write(addr, value) else: return self.ppu.cpu_read(addr) # access rom otherwise if word: return self.rom.get_word(addr) return self.rom.get(addr)
from ppu import PPU from threading import Thread from queue import Queue import time data = array("B") if len(sys.argv) < 2: print("pls give rom name") sys.exit() filename = sys.argv[1] filesize = os.stat(filename).st_size print("Loading", filename, " (" + str(filesize) + " bytes)") with open(filename, 'rb') as f: data.fromfile(f, filesize) queue = Queue() render_thread = Thread(target=renderer.render, args=(queue, )) render_thread.start() memory.loadROM(data) memory.loadBootstrap() ppu = PPU(queue) cpu.run(ppu)
class LoROMMemoryMapper(object): def __init__(self, RAM, ROM, SRAM, use_MAD1_mapping, SRAM_size): self.RAM = RAM # TODO: maybe rename to WRAM self.ROM = ROM self.SRAM = SRAM self.use_MAD1_mapping = use_MAD1_mapping self.SRAM_size = SRAM_size self.dma = DMAController() self.hdm = HDMAController() self.internal_cpu_registers = InternalCPURegisters() self.ppu = PPU() def read(self, bank, offset): if bank >= 0x00 and bank <= 0x3F: return self.read_system(bank, offset) elif bank >= 0x40 and bank <= 0x6F: return self.read_ROM(bank, offset) elif bank >= 0x70 and bank <= 0x7D: return self.read_SRAM_ROM(bank, offset) elif bank >= 0x7E and bank <= 0x7F: return self.read_RAM(bank, offset) elif bank >= 0x80 and bank <= 0xFF: return self.read_upper_mirror(bank, offset) else: raise IllegalAddressExcpetion() # 0x00:0000 - 3F:FFFF read ROM and system stuff # TODO: the doc on the internet is very inconsistent about the memory ranges def read_system(self, bank, offset): if offset <= 0x1FFF: return self.RAM[offset] elif offset >= 0x2000 and offset <= 0x2FFF: # maybe 21FF is correct # TODO: PPU, APU, Hardware Registers # 0x2100 - 0x213F PPU (or PPU2 ?) # 0x2180 - 0x2183 (insde RAM?) # raise NotImplementedError() return self.ppu.read(offset) elif offset >= 0x3000 and offset <= 0x3FFF: # TODO: Super-FX, DSP raise NotImplementedError() elif offset >= 0x4000 and offset <= 0x41FF: # maybe 40FF is correct # TODO: Joypad Registers / Controller # 0x4016 - 0x4017 CPU raise NotImplementedError() elif offset >= 0x4200 and offset <= 0x5FFF: # maybe 44FF is correct # TODO: DMA, PPU2, Hardware Registers # 0x4200 - 0x420D CPU # 0x4100 - 0x421F CPU # 0x4300 - 0x437F CPU if offset == 0x4200: return self.internal_cpu_registers.read(offset) elif offset >= 0x4300 and offset <= 0x43FF: return self.dma.read(offset) print("Error read Address: " + hex(offset)) return 0 elif offset >= 0x6000 and offset <= 0x7FFF: # TODO: enhancement chip memory raise NotImplementedError() elif offset >= 0x8000: # read ROM ROM_bank = (bank) * 0x8000 return self.ROM[ROM_bank + (offset - 0x8000)] else: raise IllegalAddressExcpetion() # 0x40:0000 - 6F:FFFF read ROM (only 32 KB in 64KB, the other half is "maybe" mirrored) def read_ROM(self, bank, offset): if offset <= 0x7FFF and not self.use_MAD1_mapping: # read ROM ROM_bank = 0x20000 + (bank - 0x40) * 0x10000 return self.ROM[ROM_bank + offset] elif offset >= 0x8000: # read ROM ROM_bank = 0x20000 + (bank - 0x40) * 0x10000 return self.ROM[ROM_bank + offset] else: raise IllegalAddressExcpetion() # 0x70:0000 - 7D:FFFF reads the SRAM inside the cartirge or the ROM def read_SRAM_ROM(self, bank, offset): if offset >= 0x0000 and offset <= 0x7FFF: # read SRAM # if the SRAM is smaller than 32Kbyte it is repeated on and on (SRAM mirror) return self.SRAM[offset % self.SRAM_size] elif offset >= 0x8000 and offset <= 0xFFFF: # read ROM from 38:XXXX in 32KB chunks ROM_bank = 0x380000 + (bank - 0x70) * 0x8000 return self.ROM[ROM_bank + (offset - 0x8000)] else: raise IllegalAddressExcpetion() # 0x7E:0000 - 0x7F:FFFF read the RAM inside the SNES def read_RAM(self, bank, offset): # 8KB LowRAM, 24KB HighRAM, 96KB ExRAM if bank == 0x7E: return self.RAM[offset] elif bank == 0x7F: return self.RAM[0x8000 + offset] else: raise IllegalAddressExcpetion() # 0x80:0000 - 0xFF:FFFF mirror (same as self.read except RAM) def read_upper_mirror(self, bank, offset): if bank >= (0x00 + 0x80) and bank <= (0x3F + 0x80): return self.read_system(bank - 0x80, offset) elif bank >= (0x40 + 0x80) and bank <= (0x6F + 0x80): return self.read_ROM(bank - 0x80, offset) elif bank >= (0x70 + 0x80) and bank <= (0x7D + 0x80): return self.read_SRAM_ROM(bank - 0x80, offset) elif bank >= (0x7E + 0x80) and bank <= (0x7F + 0x80): return self.read_more_SRAM_ROM(bank, offset) # different to self.read else: raise IllegalAddressExcpetion() # 0xFE:0000 - 0xFF:FFFF read more SRAM and ROM def read_more_SRAM_ROM(self, bank, offset): if bank == 0xFE: if offset >= 0x0000 and offset <= 0x7FFF: return self.SRAM[offset] elif offset >= 0x8000 and offset <= 0xFFFF: return self.ROM[0x3E8000 + offset] else: raise IllegalAddressExcpetion() elif bank == 0xFF: if offset >= 0x0000 and offset <= 0x7FFF: return self.SRAM[offset] elif offset >= 0x8000 and offset <= 0xFFFF: return self.ROM[0x3F0000 + offset] else: raise IllegalAddressExcpetion() else: raise IllegalAddressExcpetion() def write(self, bank, offset, value): if bank >= 0x00 and bank <= 0x3F: self.write_system(bank, offset, value) elif bank >= 0x40 and bank <= 0x6F: raise CanNotWriteROMException() elif bank >= 0x70 and bank <= 0x7D: self.write_SRAM_ROM(bank, offset, value) elif bank >= 0x7E and bank <= 0x7F: self.write_RAM(bank, offset, value) elif bank >= 0x80 and bank <= 0xFF: self.write_upper_mirror(bank, offset, value) else: raise IllegalAddressExcpetion() # 0x00:0000 - 3F:FFFF write system stuff # TODO: the doc on the internet is very inconsistent about the memory ranges # TODO: do we need the bak arg? def write_system(self, bank, offset, value): if offset <= 0x1FFF: self.RAM[offset] = value elif offset >= 0x2000 and offset <= 0x2FFF: # maybe 21FF is correct # TODO: PPU, APU, Hardware Registers # 0x2100 - 0x213F PPU (or PPU2 ?) # 0x2180 - 0x2183 (insde RAM?) # raise NotImplementedError() self.ppu.write(offset, value) elif offset >= 0x3000 and offset <= 0x3FFF: # TODO: Super-FX, DSP raise NotImplementedError() elif offset >= 0x4000 and offset <= 0x41FF: # maybe 40FF is correct # TODO: Joypad Registers / Controller # 0x4016 - 0x4017 CPU raise NotImplementedError() elif offset >= 0x4200 and offset <= 0x5FFF: # maybe 44FF is correct # TODO: DMA, PPU2, Hardware Registers # 0x4200 - 0x420D CPU # 0x4100 - 0x421F CPU if offset == 0x4200: self.internal_cpu_registers.write(offset, value) return elif offset >= 0x4300 and offset <= 0x43FF: self.dma.write(offset, value) return # 0x4300 - 0x437F CPU print("Error write Address: " + hex(offset) + str(value)) elif offset >= 0x6000 and offset <= 0x7FFF: # TODO: enhancement chip memory raise NotImplementedError() elif offset >= 0x8000: # write ROM raise CanNotWriteROMException() else: raise IllegalAddressExcpetion() # 0x70:0000 - 7D:FFFF writes the SRAM inside the cartirge or the ROM def write_SRAM_ROM(self, bank, offset, value): if offset >= 0x0000 and offset <= 0x7FFF: # write SRAM # if the SRAM is smaller than 32Kbyte it is repeated on and on (SRAM mirror) self.SRAM[offset % self.SRAM_size] = value elif offset >= 0x8000 and offset <= 0xFFFF: # write ROM raise CanNotWriteROMException() else: raise IllegalAddressExcpetion() # 0x7E:0000 - 0x7F:FFFF writes the RAM inside the SNES def write_RAM(self, bank, offset, value): # 8KB LowRAM, 24KB HighRAM, 96KB ExRAM if bank == 0x7E: self.RAM[offset] = value elif bank == 0x7F: self.RAM[0x8000 + offset] = value else: raise IllegalAddressExcpetion() # 0x80:0000 - 0xFF:FFFF mirror (same as self.write except RAM) def write_upper_mirror(self, bank, offset, value): if bank >= (0x00 + 0x80) and bank <= (0x3F + 0x80): self.write_system(bank - 0x80, offset, value) elif bank >= (0x40 + 0x80) and bank <= (0x6F + 0x80): # write ROM raise CanNotWriteROMException() elif bank >= (0x70 + 0x80) and bank <= (0x7D + 0x80): self.write_SRAM_ROM(bank - 0x80, offset, value) elif bank >= (0x7E + 0x80) and bank <= (0x7F + 0x80): self.write_more_SRAM_ROM(bank, offset, value) # different to self.write else: raise IllegalAddressExcpetion() # 0xFE:0000 - 0xFF:FFFF read more SRAM and ROM def write_more_SRAM_ROM(self, bank, offset, value): if bank == 0xFE: if offset >= 0x0000 and offset <= 0x7FFF: self.SRAM[offset] = value elif offset >= 0x8000 and offset <= 0xFFFF: # write ROM raise CanNotWriteROMException() else: raise IllegalAddressExcpetion() elif bank == 0xFF: if offset >= 0x0000 and offset <= 0x7FFF: self.SRAM[offset] = value elif offset >= 0x8000 and offset <= 0xFFFF: # write ROM raise CanNotWriteROMException() else: raise IllegalAddressExcpetion() else: raise IllegalAddressExcpetion()