def __init__(self, rom_file): signal.signal(signal.SIGINT, self.sigint_handler) self.commands = { "[di]sassemble": ("Disassemble: disassemble addr[, count=16]", self.cmd_disassemble), "[r]egisters": ("Examine registers", self.cmd_registers), "[s]tep": ("One CPU step: step [count=1]", self.cmd_step), "[v]ideo": ("Examine video memory contents", self.cmd_video), "[du]mp": ("Dump memory: dump addr[, count=16]", self.cmd_dump), "[h]elp": ("This help", self.cmd_help), "[q]uit": ("Quit", self.cmd_quit), } self.video = Video() screen = pygame.display.set_mode(self.video.get_mode()) #pygame.mouse.set_visible(0) pygame.display.update() surface = pygame.Surface(screen.get_size()) surface = surface.convert() screen.blit(surface, (0, 0)) self.video.set_surface(surface) pygame.display.flip() self.cpu = CPU(self.video) fd = open(rom_file, "rb") self.cpu.load(fd.read())
def __init__(self, scaling_factor: int, cycles_per_frame: int, starting_address: int): pygame.init() self.screen = Screen(scaling_factor) self.keyboard = Keyboard() self.sound = Sound() self.cpu = CPU(self.screen, self.keyboard, starting_address) self.cycles_per_frame = cycles_per_frame sixty_hertz_ms = round(1000 / SIXTY_HERTZ) pygame.time.set_timer(SIXTY_HERTZ_CLOCK, sixty_hertz_ms) print( f"Target CPU speed: {cycles_per_frame * SIXTY_HERTZ} instructions per second" ) print(f"Screen scaling factor: {scaling_factor}")
def test_init(self): self.assertEqual(cpu.MEMORY_SIZE, len(self.cpu.memory)) self.assertEqual(bytearray(cpu.font_sprites), self.cpu.memory[0:len(cpu.font_sprites)]) self.assertEqual([], self.cpu.stack) self.assertEqual(0x200, self.cpu.pc) self.assertEqual(0x600, CPU(Screen(), Keyboard(), starting_address=0x600).pc) self.assertEqual([0x00] * 16, self.cpu.V) self.assertEqual(0x000, self.cpu.I) self.assertEqual(0x000, self.cpu.I) self.assertEqual(0x00, self.cpu.delay_timer) self.assertEqual(0x00, self.cpu.sound_timer) self.assertEqual(0x042, CPU(Screen(), Keyboard(), starting_address=0x042).pc)
def setUp(self): self.cpu = CPU()
class TestCPU(unittest.TestCase): """ A class for testing the instructions of the CHIP-8 CPU """ def setUp(self): self.cpu = CPU() def test_00E0(self): # Draw to every other pixel on the screen for i in range(len(self.cpu.gfx)): if i % 2 == 0: self.cpu.gfx[i] = 0x1 # Clear the screen, then test it self.cpu._00E0() for i in range(len(self.cpu.gfx)): self.assertEqual(0x0, self.cpu.gfx[i]) def test_00EE(self): # Enter a subroutine, then immediately exit # Check the values of the sp and pc self.cpu._2NNN(0x2300) self.cpu._00EE() self.assertEqual(0, self.cpu.sp) self.assertEqual(0x202, self.cpu.pc) def test_1NNN(self): self.cpu._1NNN(0x1100) self.assertEqual(0x100, self.cpu.pc) def test_2NNN(self): self.cpu._2NNN(0x2100) self.assertEqual(1, self.cpu.sp) # pc is initially located at 0x200, which is now stored on the stack self.assertEqual(0x200, self.cpu.stack[self.cpu.sp - 1]) self.assertEqual(0x000, self.cpu.stack[self.cpu.sp]) self.assertEqual(0x100, self.cpu.pc) def test_3XNN(self): # Test failure of v[x] == NN self.cpu._3XNN(0x3010) self.assertEqual(0x202, self.cpu.pc) # Test success of v[x] == NN self.cpu._6XNN(0x6010) self.cpu._3XNN(0x3010) # A successful skip moves the pc by 2 bytes # Since there were two instructions, pc is moved an additional 4 bytes # 0x202 + (3 * 0x2) = 0x208 self.assertEqual(0x208, self.cpu.pc) def test_4XNN(self): # Test failure of v[x] != NN self.cpu._4XNN(0x4000) self.assertEqual(0x202, self.cpu.pc) # Test success of v[x] != NN self.cpu._6XNN(0x6010) self.cpu._4XNN(0x4000) # A successful skip moves the pc by 2 bytes # Since there were two instructions, pc is moved an additional 4 bytes # 0x202 + (3 * 0x2) = 0x208 self.assertEqual(0x208, self.cpu.pc) def test_5XY0(self): # Test success of v[x] == v[y] # A successful skip moves the pc by 2 bytes # 0x200 + (2 * 0x2) = 0x204 self.cpu._5XY0(0x5010) self.assertEqual(0x204, self.cpu.pc) # Test failure of v[x] == v[y] # Two instructions moves the pc by 4 bytes, so 0x204 + (2 * 0x2) = 0x208 self.cpu._6XNN(0x6010) self.cpu._5XY0(0x5010) self.assertEqual(0x208, self.cpu.pc) def test_6XNN(self): self.cpu._6XNN(0x6010) self.assertEqual(0x10, self.cpu.v[0]) self.assertEqual(0x202, self.cpu.pc) def test_7XNN(self): # Test without overflow self.cpu._7XNN(0x70FF) self.assertEqual(0xFF, self.cpu.v[0]) self.assertEqual(0x202, self.cpu.pc) # Test with overflow self.cpu._7XNN(0x7001) self.assertEqual(0x00, self.cpu.v[0]) self.assertEqual(0x204, self.cpu.pc) def test_8XY0(self): self.cpu._6XNN(0x61FF) self.cpu._8XY0(0, 1) self.assertEqual(0xFF, self.cpu.v[0]) self.assertEqual(0x204, self.cpu.pc) def test_8XY1(self): # v[0] = 0xF0 # v[1] = 0X0F self.cpu._6XNN(0x60F0) self.cpu._6XNN(0x610F) # v[0] | v[1] = 0XFF self.cpu._8XY1(0, 1) self.assertEqual(0xFF, self.cpu.v[0]) self.assertEqual(0x206, self.cpu.pc) def test_8XY2(self): # v[0] = 0xF1 # v[1] = 0x0F self.cpu._6XNN(0x60F1) self.cpu._6XNN(0x610F) # v[0] & v[1] = 0x01 self.cpu._8XY2(0, 1) self.assertEqual(0x01, self.cpu.v[0]) self.assertEqual(0x206, self.cpu.pc) def test_8XY3(self): # v[0] = 0x10 # v[1] = 0x20 self.cpu._6XNN(0x6010) self.cpu._6XNN(0x6120) # v[0] ^ v[1] = 0x01 self.cpu._8XY3(0, 1) self.assertEqual(0x30, self.cpu.v[0]) self.assertEqual(0x206, self.cpu.pc) def test_8XY4(self): # Test without carry self.cpu._6XNN(0x60F0) self.cpu._6XNN(0x610F) self.cpu._8XY4(0, 1) self.assertEqual(0xFF, self.cpu.v[0]) self.assertEqual(0x00, self.cpu.v[0xF]) self.assertEqual(0x206, self.cpu.pc) # Test with carry self.cpu._6XNN(0x6110) self.cpu._8XY4(0, 1) self.assertEqual(0x0F, self.cpu.v[0]) self.assertEqual(0x01, self.cpu.v[0xF]) self.assertEqual(0x20A, self.cpu.pc) def test_8XY5(self): # Test without borrow self.cpu._6XNN(0x60F0) self.cpu._6XNN(0x61E0) self.cpu._8XY5(0, 1) self.assertEqual(0x10, self.cpu.v[0]) self.assertEqual(0x01, self.cpu.v[0xF]) self.assertEqual(0x206, self.cpu.pc) # Test with borrow self.cpu._6XNN(0x61F0) self.cpu._8XY5(0, 1) self.assertEqual(0x20, self.cpu.v[0]) self.assertEqual(0x00, self.cpu.v[0xF]) self.assertEqual(0x20A, self.cpu.pc) def test_8XY6(self): # v[1] = 0x01 self.cpu._6XNN(0x6101) self.cpu._8XY6(0, 1) self.assertEqual(0x01, self.cpu.v[0xF]) self.assertEqual(0x2, self.cpu.v[1]) self.assertEqual(0x2, self.cpu.v[0]) self.assertEqual(0x204, self.cpu.pc) def test_8XY7(self): # Test without borrow self.cpu._6XNN(0x60E0) self.cpu._6XNN(0x61F0) self.cpu._8XY7(0, 1) self.assertEqual(0x10, self.cpu.v[0]) self.assertEqual(0x01, self.cpu.v[0xF]) self.assertEqual(0x206, self.cpu.pc) # Test with borrow self.cpu._6XNN(0x60F5) self.cpu._8XY7(0, 1) self.assertEqual(0xFB, self.cpu.v[0]) self.assertEqual(0x00, self.cpu.v[0xF]) self.assertEqual(0x20A, self.cpu.pc) def test_8XYE(self): # v[1] = 0x80 self.cpu._6XNN(0x6180) self.cpu._8XYE(0, 1) self.assertEqual(0x01, self.cpu.v[0xF]) self.assertEqual(0x40, self.cpu.v[1]) self.assertEqual(0x40, self.cpu.v[0]) self.assertEqual(0x204, self.cpu.pc) def test_9XY0(self): # Test failure of v[x] != v[y] self.cpu._9XY0(0x9010) self.assertEqual(0x202, self.cpu.pc) # Test success of v[x] != v[y] self.cpu._6XNN(0x6010) self.cpu._9XY0(0x9010) # A successful skip moves the pc by 2 bytes # Since there were two instructions, pc is moved an additional 4 bytes # 0x202 + (3 * 0x2) = 0x208 self.assertEqual(0x208, self.cpu.pc) def test_ANNN(self): self.cpu._ANNN(0xAFFF) self.assertEqual(0xFFF, self.cpu.i) self.assertEqual(0x202, self.cpu.pc) def test_BNNN(self): self.cpu._6XNN(0x600F) self.cpu._BNNN(0xBFF0) self.assertEqual(0xFFF, self.cpu.pc) def test_CXNN(self): self.cpu._CXNN(0xC015) self.assertTrue(self.cpu.v[0] >= 0 and self.cpu.v[0] <= 255) self.assertEqual(0x202, self.cpu.pc) def test_EX9E(self): # Test failure of the key at v[x] being pressed self.cpu._EX9E(0) self.assertEqual(0x202, self.cpu.pc) # Test success of the key at v[x] being pressed # We will directly set the values of the cpu to simulate this case self.cpu.keys[0] = True self.cpu._EX9E(0) self.assertEqual(0x206, self.cpu.pc) def test_EXA1(self): # Test success of the key at v[x] not being pressed self.cpu._EXA1(0) self.assertEqual(0x204, self.cpu.pc) # Test failure of the key at v[x] not being pressed # We will directly set the values of the cpu to simulate this case self.cpu.keys[0] = True self.cpu._EXA1(0) self.assertEqual(0x206, self.cpu.pc) def test_FX07(self): # Set the delay timer directly self.cpu.delay = 1 self.cpu._FX07(0) self.assertEqual(1, self.cpu.v[0]) self.assertEqual(0x202, self.cpu.pc) def test_FX0A(self): pass def test_FX15(self): self.cpu._6XNN(0x6010) self.cpu._FX15(0) self.assertEqual(0x10, self.cpu.delay) self.assertEqual(0x204, self.cpu.pc) def test_FX18(self): self.cpu._6XNN(0x6010) self.cpu._FX18(0) self.assertEqual(0x10, self.cpu.sound) self.assertEqual(0x204, self.cpu.pc) def test_FX1E(self): self.cpu._6XNN(0x6010) self.cpu._ANNN(0xA100) self.cpu._FX1E(0) self.assertEqual(0x110, self.cpu.i) self.assertEqual(0x206, self.cpu.pc) def test_FX29(self): self.cpu._6XNN(0x6010) self.cpu._FX29(0) self.assertEqual(0x50, self.cpu.i) self.assertEqual(0x204, self.cpu.pc) def test_FX33(self): # Place 245 into V0. self.cpu._6XNN(0x60F5) self.cpu._FX33(0) # We expect 2, 4, 5 to be located at i, i + 1, and i + 2, respectively self.assertEqual(2, self.cpu.memory[self.cpu.i]) self.assertEqual(4, self.cpu.memory[self.cpu.i + 1]) self.assertEqual(5, self.cpu.memory[self.cpu.i + 2]) self.assertEqual(0x204, self.cpu.pc) def test_FX55(self): self.cpu._6XNN(0x6001) self.cpu._6XNN(0x6102) self.cpu._6XNN(0x6203) self.cpu._6XNN(0x6304) j = self.cpu.i self.cpu._FX55(3) self.assertEqual(1, self.cpu.memory[j]) self.assertEqual(2, self.cpu.memory[j + 1]) self.assertEqual(3, self.cpu.memory[j + 2]) self.assertEqual(4, self.cpu.memory[j + 3]) self.assertEqual(4, self.cpu.i) self.assertEqual(0x20A, self.cpu.pc) def test_FX65(self): for j in range(3): self.cpu.memory[self.cpu.i + j] = j self.cpu._FX65(2) self.assertEqual(0, self.cpu.v[0]) self.assertEqual(1, self.cpu.v[1]) self.assertEqual(2, self.cpu.v[2]) self.assertEqual(0, self.cpu.v[3]) self.assertEqual(3, self.cpu.i) self.assertEqual(0x202, self.cpu.pc)
class Debugger: def __init__(self, rom_file): signal.signal(signal.SIGINT, self.sigint_handler) self.commands = { "[di]sassemble": ("Disassemble: disassemble addr[, count=16]", self.cmd_disassemble), "[r]egisters": ("Examine registers", self.cmd_registers), "[s]tep": ("One CPU step: step [count=1]", self.cmd_step), "[v]ideo": ("Examine video memory contents", self.cmd_video), "[du]mp": ("Dump memory: dump addr[, count=16]", self.cmd_dump), "[h]elp": ("This help", self.cmd_help), "[q]uit": ("Quit", self.cmd_quit), } self.video = Video() screen = pygame.display.set_mode(self.video.get_mode()) #pygame.mouse.set_visible(0) pygame.display.update() surface = pygame.Surface(screen.get_size()) surface = surface.convert() screen.blit(surface, (0, 0)) self.video.set_surface(surface) pygame.display.flip() self.cpu = CPU(self.video) fd = open(rom_file, "rb") self.cpu.load(fd.read()) def run(self): last_command = None while True: try: prompt = "(debugger) " raw = raw_input(prompt) if (raw == '') and (not last_command is None): callback, callback_args = last_command callback(*callback_args) else: args = shlex.split(raw) callback = None for k, v in self.commands.items(): m = re.search("^(.*)\[(.*)\](.*)$", k) short = m.group(2) long = m.group(1) + m.group(2) + m.group(3) if args[0] in [short, long]: callback = v[1] # we assume that command arguments are always numeric either in hex 0x... or decimal format callback_args = [] for arg in args[1:]: try: arg = int(arg, 0) except (ValueError, TypeError): pass callback_args.append(arg) last_command = callback, callback_args callback(*callback_args) break else: raise CommandException("Unknown command %s" % args[0]) except IndexError: continue except CommandException as e: print e except (EOFError, ExitException): return 0 except SIGINTException: print >> sys.stderr, \ "<SIGINT received, bye-bye>" return 1 def sigint_handler(self, signum, frame): """Handler for the SIGINT signal.""" raise SIGINTException def print_addr(self, addr): instruction = self.cpu.get_instruction(addr) decoded = self.cpu.decode_instruction(instruction) print "0x%04X 0x%04X" % (addr, instruction), if decoded is None: print "*** Unknown instruction" else: (handler, args) = decoded syntax = re.split('\n', inspect.getdoc(handler))[0] fmt = syntax\ .replace('Vx', 'V%d')\ .replace('Vy', 'V%d')\ .replace('addr', '0x%04X')\ .replace('nibble', '%d')\ .replace('byte', '0x%02X')\ print fmt % tuple(args), if addr == self.cpu.IP: print " <<<", print def cmd_disassemble(self, *args): if (len(args) < 1) or (len(args) > 2): raise BadArgumentCountException addr = args[0] count = args[1] if len(args) == 2 else 16 while (count > 0) and (addr < len(self.cpu.ram) - 2): self.print_addr(addr) count -= 1 addr += 2 def cmd_registers(self, *args): if len(args) > 0: raise BadArgumentsException for i in range(0, len(self.cpu.V)): print "V%d: 0x%02x" % (i, self.cpu.V[i]) print "I: 0x%04X" % self.cpu.I print "DT: 0x%02X" % self.cpu.DT print "ST: 0x%02X" % self.cpu.ST print "(IP): 0x%04X" % self.cpu.IP print "(SP): 0x%02X" % self.cpu.SP def cmd_dump(self, *args): if (len(args) < 1) or (len(args) > 2): raise BadArgumentCountException addr = args[0] count = args[1] if len(args) == 2 else 16 i = 0 while (i < count) and (addr < len(self.cpu.ram) - 2): if ((i % 16) == 0) and (i > 0): print print "%02X" % self.cpu.ram[addr], i += 1 addr += 2 print def cmd_video(self, *args): if len(args) > 0: raise BadArgumentCountException for y in xrange(0, 32): for x in xrange(0, 64): print 'x' if self.video.get_pixel(x, y) else '.', print print def cmd_step(self, *args): if len(args) > 1: raise BadArgumentCountException if len(args) == 0: count = 1 else: count = args[0] while count > 0: self.print_addr(self.cpu.IP) self.cpu.tick() count -= 1 def cmd_help(self, *args): if len(args) > 0: raise NoArgumentsRequredException for k, v in self.commands.items(): print k, print " " * (12 - len(k)), print v[0] def cmd_quit(self, *args): raise ExitException
def main(rom_file): pygame.init() #screen = pygame.display.set_mode((64, 64)) video = Video() screen = pygame.display.set_mode(video.get_mode()) #pygame.mouse.set_visible(0) pygame.display.update() surface = pygame.Surface(screen.get_size()) surface = surface.convert() screen.blit(surface, (0, 0)) video.set_surface(surface) pygame.display.flip() keyboard = Keyboard() computer = CPU(video) fd = open(rom_file, "rb") computer.load(fd.read()) CLOCK_HZ = 10000 # 4194304 TIMER_HZ = 30 CPU_CLOCK_EVENT = USEREVENT + 1 TIMER_CLOCK_EVENT = CPU_CLOCK_EVENT + 1 #pygame.time.set_timer(CPU_CLOCK_EVENT, 1000 / CLOCK_HZ) #pygame.time.set_timer(TIMER_CLOCK_EVENT, int(1000 / TIMER_HZ)) pygame.time.set_timer(TIMER_CLOCK_EVENT, 100) i = 0 while True: event = pygame.event.poll() if event.type == pygame.QUIT: sys.exit() #elif event.type == CPU_CLOCK_EVENT: # computer.tick() elif event.type == TIMER_CLOCK_EVENT: computer.timer() elif event.type == pygame.KEYUP: key = keyboard.translate(event.key) if not key is None: computer.key_up(key) elif event.type == pygame.KEYDOWN: key = keyboard.translate(event.key) if not key is None: computer.key_down(key) i += 1 #print "timer " + str(i) computer.timer() computer.tick() #pygame.time.wait(1000 / CLOCK_HZ) screen.blit(surface, (0, 0)) if not video.dirty_rect is None: pygame.display.update(video.dirty_rect) video.dirty_rect = None
class CPUTest(unittest.TestCase): def setUp(self): self.cpu = CPU() def test_int2hex(self): self.assertEquals('0000', self.cpu.int2hex(0)) self.assertEquals('0001', self.cpu.int2hex(1)) self.assertEquals('00FF', self.cpu.int2hex(255)) self.assertEquals('0100', self.cpu.int2hex(256)) self.assertEquals('0FFF', self.cpu.int2hex(0x0FFF)) self.assertEquals('FFFF', self.cpu.int2hex(0xFFFF)) self.assertEquals('A123', self.cpu.int2hex(0xA123)) def test_decode_instruction(self): self.assertEquals((self.cpu.CLS, []), self.cpu.decode_instruction(0x00E0)) # CLS self.assertEquals((self.cpu.JP_addr, [564]), self.cpu.decode_instruction(0x1234)) # JP 0x234 self.assertEquals((self.cpu.LD_Vx_Vy, [1, 2]), self.cpu.decode_instruction(0x8120)) # LD V1, V2 self.assertEquals((self.cpu.RND_Vx_byte, [5, 255]), self.cpu.decode_instruction(0xC5FF)) # RND V5, FF self.assertEquals((self.cpu.DRW_Vx_Vy_nibble, [4, 5, 7]), self.cpu.decode_instruction(0xD457)) # DRW V4, V5, 7 self.assertEquals((self.cpu.LD_I_Vx, [10]), self.cpu.decode_instruction(0xFA55)) # LD [I], V10 def load_example_program(self): program = '00E081201000' # CLS; LD V1, V2; JP 0000 data = bytearray(program.decode('hex')) self.cpu.load(data) def test_get_instruction(self): self.load_example_program() self.assertEquals(0x00E0, self.cpu.get_instruction(0)) self.assertEquals(0x8120, self.cpu.get_instruction(2)) self.assertEquals(0x1000, self.cpu.get_instruction(4)) def test_load_and_decode_instruction(self): self.load_example_program()
class Chip8: screen: Screen keyboard: Keyboard sound: Sound cpu: CPU cycles_per_frame: int def __init__(self, scaling_factor: int, cycles_per_frame: int, starting_address: int): pygame.init() self.screen = Screen(scaling_factor) self.keyboard = Keyboard() self.sound = Sound() self.cpu = CPU(self.screen, self.keyboard, starting_address) self.cycles_per_frame = cycles_per_frame sixty_hertz_ms = round(1000 / SIXTY_HERTZ) pygame.time.set_timer(SIXTY_HERTZ_CLOCK, sixty_hertz_ms) print( f"Target CPU speed: {cycles_per_frame * SIXTY_HERTZ} instructions per second" ) print(f"Screen scaling factor: {scaling_factor}") def load(self, rom: bytes): self.cpu.load(rom) def run(self): while True: self._handle_events() def _handle_events(self): for event in pygame.event.get(): if event.type == pygame.KEYDOWN: self.keyboard.keydown(event) elif event.type == pygame.KEYUP: key = self.keyboard.keyup(event) if self.cpu.waiting_for_keypress and key is not None: self.cpu.key_was_pressed(key) elif event.type == SIXTY_HERTZ_CLOCK: self.tick() elif event.type == pygame.QUIT: pygame.quit() sys.exit() def tick(self): if self.cpu.waiting_for_keypress: return has_screen_changed = False self.cpu.decrease_timers() for _ in range(self.cycles_per_frame): try: self.cpu.step() except UpdateScreen: has_screen_changed = True except WaitForKeypress: break self.sound.update(self.cpu.sound_timer) if has_screen_changed: self.screen.update()
def setUp(self): self.screen = Screen() self.keyboard = Keyboard() self.cpu = CPU(self.screen, self.keyboard)
class TestCPU(unittest.TestCase): def setUp(self): self.screen = Screen() self.keyboard = Keyboard() self.cpu = CPU(self.screen, self.keyboard) def test_init(self): self.assertEqual(cpu.MEMORY_SIZE, len(self.cpu.memory)) self.assertEqual(bytearray(cpu.font_sprites), self.cpu.memory[0:len(cpu.font_sprites)]) self.assertEqual([], self.cpu.stack) self.assertEqual(0x200, self.cpu.pc) self.assertEqual(0x600, CPU(Screen(), Keyboard(), starting_address=0x600).pc) self.assertEqual([0x00] * 16, self.cpu.V) self.assertEqual(0x000, self.cpu.I) self.assertEqual(0x000, self.cpu.I) self.assertEqual(0x00, self.cpu.delay_timer) self.assertEqual(0x00, self.cpu.sound_timer) self.assertEqual(0x042, CPU(Screen(), Keyboard(), starting_address=0x042).pc) def test_decrease_timers(self): self.cpu.delay_timer = 0x01 self.cpu.sound_timer = 0x01 self.cpu.decrease_timers() self.assertEqual(0x00, self.cpu.delay_timer) self.assertEqual(0x00, self.cpu.sound_timer) self.cpu.decrease_timers() self.assertEqual(0x00, self.cpu.delay_timer) self.assertEqual(0x00, self.cpu.sound_timer) def test_CLS(self): # 00E0 for row in self.screen.buffer: for x, _ in enumerate(row): row[x] = True self.cpu._CLS() for row in self.screen.buffer: self.assertTrue(not any(row)) def test_RET(self): # 00EE self.cpu.stack.append(0x42) self.cpu._RET() self.assertEqual(0x42, self.cpu.pc) self.assertEqual([], self.cpu.stack) def test_JP_nnn(self): # 1nnn self.cpu.instruction = 0x1042 self.cpu._JP_nnn() self.assertEqual(0x42, self.cpu.pc) def test_CALL_nnn(self): # 2nnn self.cpu.instruction = 0x2042 self.cpu.pc = 0x1234 self.cpu._CALL_nnn() self.assertEqual(0x42, self.cpu.pc) self.assertEqual([0x1234], self.cpu.stack) def test_SE_Vx_nn(self): # 3xnn addr = self.cpu.pc self.cpu.V[0x1] = 0x42 self.cpu.instruction = 0x3042 self.cpu._SE_Vx_nn() self.assertEqual(addr, self.cpu.pc) self.cpu.instruction = 0x3141 self.cpu._SE_Vx_nn() self.assertEqual(addr, self.cpu.pc) self.cpu.instruction = 0x3142 self.cpu._SE_Vx_nn() self.assertEqual(addr + 2, self.cpu.pc) def test_SNE_Vx_nn(self): # 4xnn addr = self.cpu.pc self.cpu.V[0x1] = 0x42 self.cpu.instruction = 0x4142 self.cpu._SNE_Vx_nn() self.assertEqual(addr, self.cpu.pc) self.cpu.instruction = 0x4042 self.cpu._SNE_Vx_nn() self.assertEqual(addr + 2, self.cpu.pc) self.cpu.instruction = 0x4141 self.cpu._SNE_Vx_nn() self.assertEqual(addr + 4, self.cpu.pc) def test_SE_Vx_Vy(self): # 5xy0 self.cpu.instruction = 0x5120 addr = self.cpu.pc self.cpu.V[0x1] = 0x41 self.cpu.V[0x2] = 0x42 self.cpu._SE_Vx_Vy() self.assertEqual(addr, self.cpu.pc) self.cpu.V[0x1] = 0x42 self.cpu._SE_Vx_Vy() self.assertEqual(addr + 2, self.cpu.pc) def test_LD_Vx_nn(self): # 6xnn self.cpu.instruction = 0x6142 self.cpu._LD_Vx_nn() self.assertEqual(0x42, self.cpu.V[0x1]) self.cpu._LD_Vx_nn() self.assertEqual(0x42, self.cpu.V[0x1]) def test_ADD_Vx_nn(self): # 7xnn self.cpu.instruction = 0x7101 self.cpu.V[0x1] = 0xfe self.cpu._ADD_Vx_nn() self.assertEqual(0xff, self.cpu.V[0x1]) self.assertEqual(0x00, self.cpu.V[0xf]) self.cpu._ADD_Vx_nn() self.assertEqual(0x00, self.cpu.V[0x1]) self.assertEqual(0x00, self.cpu.V[0xf]) def test_LD_Vx_Vy(self): # 8xy0 self.cpu.instruction = 0x8010 self.cpu.V[0x1] = 0x42 self.cpu._LD_Vx_Vy() self.assertEqual(0x42, self.cpu.V[0x0]) def test_OR_Vx_Vy(self): # 8xy1 self.cpu.instruction = 0x8121 self.cpu.V[0x1] = 0b01010101 self.cpu.V[0x2] = 0b00001111 self.cpu._OR_Vx_Vy() self.assertEqual(0b01011111, self.cpu.V[0x1]) def test_AND_Vx_Vy(self): # 8xy2 self.cpu.instruction = 0x8122 self.cpu.V[0x1] = 0b01010101 self.cpu.V[0x2] = 0b00001111 self.cpu._AND_Vx_Vy() self.assertEqual(0b00000101, self.cpu.V[0x1]) def test_XOR_Vx_Vy(self): # 8xy3 self.cpu.instruction = 0x8123 self.cpu.V[0x1] = 0b01010101 self.cpu.V[0x2] = 0b00001111 self.cpu._XOR_Vx_Vy() self.assertEqual(0b01011010, self.cpu.V[0x1]) def test_ADD_Vx_Vy(self): # 8xy4 self.cpu.instruction = 0x8124 self.cpu.V[0x1] = 0xfe self.cpu.V[0x2] = 0x01 self.cpu._ADD_Vx_Vy() self.assertEqual(0xff, self.cpu.V[0x1]) self.assertEqual(0x00, self.cpu.V[0xf]) self.cpu._ADD_Vx_Vy() self.assertEqual(0x00, self.cpu.V[0x1]) self.assertEqual(0x01, self.cpu.V[0xf]) def test_SUB_Vx_Vy(self): # 8xy5 self.cpu.instruction = 0x8125 self.cpu.V[0x1] = 0x01 self.cpu.V[0x2] = 0x01 self.cpu._SUB_Vx_Vy() self.assertEqual(0x00, self.cpu.V[0x1]) self.assertEqual(0x01, self.cpu.V[0xf]) self.cpu._SUB_Vx_Vy() self.assertEqual(0xff, self.cpu.V[0x1]) self.assertEqual(0x00, self.cpu.V[0xf]) def test_SHR_Vx_Vy(self): # 8xy6 self.cpu.instruction = 0x8126 self.cpu.V[0x2] = 0b11110000 self.cpu._SHR_Vx_Vy() self.assertEqual(0b01111000, self.cpu.V[0x1]) self.assertEqual(0b00000000, self.cpu.V[0xf]) self.cpu.V[0x2] = 0b00001111 self.cpu._SHR_Vx_Vy() self.assertEqual(0b00000111, self.cpu.V[0x1]) self.assertEqual(0b00000001, self.cpu.V[0xf]) def test_SUBN_Vx_Vy(self): # 8xy7 self.cpu.instruction = 0x8127 self.cpu.V[0x1] = 0x01 self.cpu.V[0x2] = 0x01 self.cpu._SUBN_Vx_Vy() self.assertEqual(0x00, self.cpu.V[0x1]) self.assertEqual(0x01, self.cpu.V[0xf]) self.cpu.V[0x1] = 0x02 self.cpu._SUBN_Vx_Vy() self.assertEqual(0xff, self.cpu.V[0x1]) self.assertEqual(0x00, self.cpu.V[0xf]) def test_SHL_Vx_Vy(self): # 8xyE self.cpu.instruction = 0x812E self.cpu.V[0x2] = 0b11110000 self.cpu._SHL_Vx_Vy() self.assertEqual(0b11100000, self.cpu.V[0x1]) self.assertEqual(0b00000001, self.cpu.V[0xf]) self.cpu.V[0x2] = 0b00001111 self.cpu._SHL_Vx_Vy() self.assertEqual(0b00011110, self.cpu.V[0x1]) self.assertEqual(0b00000000, self.cpu.V[0xf]) def test_SNE_Vx_Vy(self): # 9xy0 self.cpu.instruction = 0x9120 addr = self.cpu.pc self.cpu.V[0x1] = 0x42 self.cpu.V[0x2] = 0x42 self.cpu._SNE_Vx_Vy() self.assertEqual(addr, self.cpu.pc) self.cpu.V[0x1] = 0x41 self.cpu._SNE_Vx_Vy() self.assertEqual(addr + 2, self.cpu.pc) def test_LD_I_nnn(self): # Annn self.cpu.instruction = 0xA042 self.cpu._LD_I_nnn() self.assertEqual(0x42, self.cpu.I) def test_JP_V0_nnn(self): # Bnnn self.cpu.instruction = 0xB042 self.cpu.V[0x0] = 0x42 self.cpu._JP_V0_nnn() self.assertEqual(0x42 + 0x42, self.cpu.pc) def test_RND_Vx_nn(self): # Cxnn numbers = set() for mask in range(0xff + 1): self.cpu.instruction = 0xC100 | mask self.cpu._RND_Vx_nn() rnd = self.cpu.V[0x1] self.assertEqual(rnd & mask, rnd) numbers.add(rnd) self.assertGreater(len(numbers), 50) def test_DRW_Vx_Vy_n(self): # Dxyn to_int = lambda bool_list: sum( [val << (7 - pos) for pos, val in enumerate(bool_list)]) x = 4 y = 2 digit = 7 self.cpu.instruction = 0xD125 self.cpu.I = digit * 5 self.cpu.V[0x1] = x self.cpu.V[0x2] = y with self.assertRaises(UpdateScreen): self.cpu._DRW_Vx_Vy_n() for line in range(5): self.assertEqual(cpu.font_sprites[digit * 5 + line], to_int(self.screen.buffer[y + line][x:x + 8])) self.assertEqual(0x00, self.cpu.V[0xf]) with self.assertRaises(UpdateScreen): self.cpu._DRW_Vx_Vy_n() for line in range(5): self.assertEqual(0x00, to_int(self.screen.buffer[y + line][x:x + 8])) self.assertEqual(0x01, self.cpu.V[0xf]) self.cpu.V[0x1] = 62 self.cpu.V[0x2] = 30 with self.assertRaises(UpdateScreen): self.cpu._DRW_Vx_Vy_n() self.assertEqual([True, True], self.screen.buffer[30][62:64]) self.assertEqual([False, False], self.screen.buffer[31][62:64]) self.assertEqual([False, False], self.screen.buffer[0][62:64]) self.assertEqual([False, True], self.screen.buffer[1][62:64]) self.assertEqual([False, True], self.screen.buffer[2][62:64]) self.assertEqual([True, True], self.screen.buffer[30][0:2]) self.assertEqual([False, True], self.screen.buffer[31][0:2]) self.assertEqual([True, False], self.screen.buffer[0][0:2]) self.assertEqual([False, False], self.screen.buffer[1][0:2]) self.assertEqual([False, False], self.screen.buffer[2][0:2]) def test_SKP_Vx(self): # Ex9E self.cpu.instruction = 0xE19E addr = self.cpu.pc self.cpu.V[0x1] = 0x1 self.cpu._SKP_Vx() self.assertEqual(addr, self.cpu.pc) self.keyboard.pressed_keys.add(0x1) self.cpu._SKP_Vx() self.assertEqual(addr + 2, self.cpu.pc) def test_SKNP_Vx(self): # ExA1 self.cpu.instruction = 0xE1A1 addr = self.cpu.pc self.cpu.V[0x1] = 0x1 self.keyboard.pressed_keys.add(0x1) self.cpu._SKNP_Vx() self.assertEqual(addr, self.cpu.pc) self.keyboard.pressed_keys.remove(0x1) self.cpu._SKNP_Vx() self.assertEqual(addr + 2, self.cpu.pc) def test_LD_Vx_DT(self): # Fx07 self.cpu.instruction = 0xF107 self.cpu.delay_timer = 0x42 self.cpu._LD_Vx_DT() self.assertEqual(0x42, self.cpu.V[0x1]) def test_LD_Vx_K(self): # Fx0A self.cpu.instruction = 0xF10A with self.assertRaises(WaitForKeypress): self.cpu._LD_Vx_K() self.assertTrue(self.cpu.waiting_for_keypress) self.cpu.key_was_pressed(0x1) self.assertFalse(self.cpu.waiting_for_keypress) self.assertEqual(0x1, self.cpu.V[0x1]) def test_LD_DT_Vx(self): # Fx15 self.cpu.instruction = 0xF115 self.cpu.V[0x1] = 0x42 self.cpu._LD_DT_Vx() self.assertEqual(0x42, self.cpu.delay_timer) def test_LD_ST_Vx(self): # Fx18 self.cpu.instruction = 0xF118 self.cpu.V[0x1] = 0x42 self.cpu._LD_ST_Vx() self.assertEqual(0x42, self.cpu.sound_timer) def test_ADD_I_Vx(self): # Fx1E self.cpu.instruction = 0xF11E self.cpu.V[0x1] = 0x1 self.cpu.I = 0xffe self.cpu._ADD_I_Vx() self.assertEqual(0xfff, self.cpu.I) self.cpu._ADD_I_Vx() self.assertEqual(0x000, self.cpu.I) def test_LD_F_Vx(self): # Fx29 self.cpu.instruction = 0xF129 self.cpu.V[0x1] = 7 self.cpu._LD_F_Vx() self.assertEqual(7 * 5, self.cpu.I) def test_LD_B_Vx(self): # Fx33 self.cpu.instruction = 0xF133 self.cpu._LD_B_Vx() def test_LD_I_Vx(self): # Fx55 self.cpu.instruction = 0xF755 self.cpu.I = 0x123 for reg in range(0x7 + 1): self.cpu.V[reg] = reg * 2 self.cpu._LD_I_Vx() for reg in range(0x7 + 1): self.assertEqual(reg * 2, self.cpu.memory[0x123 + reg]) self.assertEqual(0x123 + 0x7 + 1, self.cpu.I) def test_LD_Vx_I(self): # Fx65 self.cpu.instruction = 0xF765 self.cpu.I = 0x123 for reg in range(0x7 + 1): self.cpu.memory[0x123 + reg] = reg * 2 self.cpu._LD_Vx_I() for reg in range(0x7 + 1): self.assertEqual(reg * 2, self.cpu.V[reg]) self.assertEqual(0x123 + 0x7 + 1, self.cpu.I)