class Machine(Entity): def __init__(self): super().__init__() nes = Nes() nes.load('roms/mario.nes') # configure ppu self._ppu_bus = Bus() self._ppu_pattern = PatternTable() self._ppu_pattern.load(nes.chr) self._ppu_name = NameTable() self._ppu_palette = PaletteTable() self._ppu_bus.connect(self._ppu_pattern) self._ppu_bus.connect(self._ppu_name) self._ppu_bus.connect(self._ppu_palette) self._ppu = Ppu(self._ppu_bus) # configure cpu self._cpu_ram = Ram() self._pgr = PGRRom() self._pgr.load(nes.pgr) self._papu_ram = PAuExp() self._cpu_bus = Bus() self._cpu_bus.connect(self._pgr) self._cpu_bus.connect(self._cpu_ram) self._cpu_bus.connect(self._papu_ram) self._cpu_bus.connect(self._ppu.get_register()) self._cpu = Cpu6502(self._cpu_bus) self._cpu.reset() self._ppu.set_request_nmi(self._cpu.request_nmi) self._addr_map, self._code = self._cpu.decode(0x8000, 0xFF00) self._font = pygame.font.SysFont('inconsolatan', 24) self._cpu_running = False self._cpu_time_last = 0 def step(self): run_cycles = self._cpu.run() for _ in range(run_cycles): self._ppu.run() self._ppu.run() self._ppu.run() return 601 * run_cycles * 50 def draw_code(self, screen): log = self._cpu.log() pc = log['PC'] if pc in self._addr_map: now_pos = self._addr_map[pc] code_x_start = 550 code_y_start = 100 code_line = 0 code_height = 20 for pos in range(now_pos - 8, now_pos + 8): if pos < 0 or pos >= len(self._code): continue if pos == now_pos: now_code = self._font.render(self._code[pos], True, (255, 0, 0)) else: now_code = self._font.render(self._code[pos], True, (0, 0, 0)) screen.blit( now_code, (code_x_start, code_line * code_height + code_y_start)) code_line += 1 def draw_flag(self, screen): flag_x_start = 620 flag_y_start = 50 width = 20 flag_tips = self._font.render('Flags:', True, (0, 0, 0)) screen.blit(flag_tips, (550, flag_y_start)) # n v - b d i z c char = 'nv-bdizc' log = self._cpu.log() flag = log['F'] for i in range(8): if ((flag >> (7 - i))) & 1 == 1: f = self._font.render(char[i], True, (0, 0, 0)) else: f = self._font.render(char[i], True, (128, 128, 128)) screen.blit(f, (flag_x_start + i * width, flag_y_start)) def draw_reg(self, screen): reg_x_start = 550 reg_y_start = 0 one_width = 100 one_height = 20 log = self._cpu.log() reg_a = self._font.render('A: ${:02X}'.format(log['A']), True, (0, 0, 0)) reg_sp = self._font.render('S: ${:02X}'.format(log['SP']), True, (0, 0, 0)) reg_x = self._font.render('X: ${:02X}'.format(log['X']), True, (0, 0, 0)) reg_y = self._font.render('Y: ${:02X}'.format(log['Y']), True, (0, 0, 0)) screen.blit(reg_a, (reg_x_start, reg_y_start)) screen.blit((reg_sp), (reg_x_start + one_width, reg_y_start)) screen.blit(reg_x, (reg_x_start, reg_y_start + one_height)) screen.blit(reg_y, (reg_x_start + one_width, reg_y_start + one_height)) def draw_ppu(self, screen): img = self._ppu.get_background(0) screen.blit(img, (0, 0)) img = self._ppu.get_background(1) screen.blit(img, (256, 0)) img = self._ppu.get_background(2) screen.blit(img, (0, 240)) img = self._ppu.get_background(3) screen.blit(img, (256, 240)) def draw_pattern(self, screen): img = self._ppu.get_pattern_image() width = int(256 * 1.125) height = int(128 * 1.125) img = pygame.transform.scale(img, (width, height)) screen.blit(img, (800 - width, 600 - height)) def draw_palettes(self, screen): img = self._ppu.get_palettes_image() img = pygame.transform.scale(img, (128, 64)) screen.blit(img, (0, 600 - 64)) def on_update(self, delta): cnt = 0 if self._cpu_running: self._cpu_time_last += delta * 1000000 while self._cpu_time_last > 0: self._cpu_time_last -= self.step() cnt += 1 # print('Times: {}, Cycles: {}'.format(delta, cnt)) def on_render(self, screen): screen.fill(PALETTES[0]) self.draw_code(screen) self.draw_reg(screen) self.draw_flag(screen) self.draw_ppu(screen) self.draw_pattern(screen) self.draw_palettes(screen) # print(self._ppu.get_register().ctrl) # print(self._ppu.get_register().status) def on_event(self, event): if event.type == pygame.locals.KEYDOWN: if event.key == pygame.locals.K_s: self._cpu_running = not self._cpu_running self._cpu_time_last = 0 elif event.key == pygame.locals.K_r: self._cpu.reset() self._cpu_time_last = 0 elif event.key == pygame.locals.K_SPACE: if not self._cpu_running: self.step()