Skip to content

Commit

Permalink
Unit tests for drawing commands.
Browse files Browse the repository at this point in the history
  • Loading branch information
craigthomas committed Jul 9, 2024
1 parent b802ca0 commit 7c5ce97
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 5 deletions.
12 changes: 8 additions & 4 deletions chip8/cpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -874,23 +874,27 @@ def draw_sprite(self):
self.last_op = f"DRAWEX"
else:
if self.bitplane == 3:
self.draw_normal(x_pos, y_pos, num_bytes, 1)
self.draw_normal(x_pos, y_pos, num_bytes, 2)
self.draw_normal(x_pos, y_pos, num_bytes, 1, index=self.index)
self.draw_normal(x_pos, y_pos, num_bytes, 2, index=self.index + num_bytes)
else:
self.draw_normal(x_pos, y_pos, num_bytes, self.bitplane)
self.last_op = f"DRAW V{x_source:01X}, V{y_source:01X}"

def draw_normal(self, x_pos, y_pos, num_bytes, bitplane):
def draw_normal(self, x_pos, y_pos, num_bytes, bitplane, index=None):
"""
Draws a sprite on the screen while in NORMAL mode.
:param x_pos: the X position of the sprite
:param y_pos: the Y position of the sprite
:param num_bytes: the number of bytes to draw
:param bitplane: the bitplane to draw to
:param index: the memory index in memory where byte the pattern is stored
"""
if not index:
index = self.index

for y_index in range(num_bytes):
color_byte = self.memory[self.index + y_index]
color_byte = self.memory[index + y_index]
y_coord = y_pos + y_index
if not self.clip_quirks or (self.clip_quirks and y_coord < self.screen.get_height()):
y_coord = y_coord % self.screen.get_height()
Expand Down
127 changes: 126 additions & 1 deletion test/test_chip8cpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,16 @@ def test_logical_or(self):
self.cpu.v[source],
source_val)

def test_logical_or_logic_quirks_clears_flag(self):
self.cpu.v[1] = 0
self.cpu.v[2] = 1
self.cpu.v[0xF] = 1
self.cpu.operand = 0x8121
self.cpu.logic_quirks = True
self.cpu.logical_or()
self.assertEqual(1, self.cpu.v[1])
self.assertEqual(0, self.cpu.v[0xF])

def test_logical_and(self):
for source in range(0x10):
for target in range(0x10):
Expand All @@ -279,6 +289,16 @@ def test_logical_and(self):
self.cpu.v[source],
source_val)

def test_logical_and_logic_quirks_clears_flag(self):
self.cpu.v[1] = 0
self.cpu.v[2] = 1
self.cpu.v[0xF] = 1
self.cpu.operand = 0x8122
self.cpu.logic_quirks = True
self.cpu.logical_and()
self.assertEqual(0, self.cpu.v[1])
self.assertEqual(0, self.cpu.v[0xF])

def test_exclusive_or(self):
for source in range(0x10):
for target in range(0x10):
Expand All @@ -294,6 +314,16 @@ def test_exclusive_or(self):
self.cpu.v[source],
source_val ^ target_val)

def test_exclusive_or_logic_quirks_clears_flag(self):
self.cpu.v[1] = 1
self.cpu.v[2] = 1
self.cpu.v[0xF] = 1
self.cpu.operand = 0x8123
self.cpu.logic_quirks = True
self.cpu.exclusive_or()
self.assertEqual(0, self.cpu.v[1])
self.assertEqual(0, self.cpu.v[0xF])

def test_add_to_reg(self):
for source in range(0xF):
for target in range(0xF):
Expand Down Expand Up @@ -814,7 +844,7 @@ def test_misc_routines_raises_exception_on_unknown_op_codes(self):
def test_save_skip_routines_raises_exception_on_unknown_op_codes(self):
self.cpu.operand = 0x50FF
with self.assertRaises(UnknownOpCodeException) as context:
self.cpu.misc_routines()
self.cpu.save_skip_routines()
self.assertEqual("Unknown op-code: 50FF", str(context.exception))

def test_scroll_down_called(self):
Expand Down Expand Up @@ -857,6 +887,101 @@ def test_draw_extended_called(self):
self.cpu.draw_sprite()
self.assertTrue(self.cpu_spy.draw_extended.assert_called)

def test_draw_sprite_normal_bitplane_1_integration_correct(self):
self.screen = Chip8Screen(2)
self.screen.init_display()
self.cpu = Chip8CPU(self.screen)

self.cpu.memory[0x0200] = 0xD0
self.cpu.memory[0x0201] = 0x01
self.cpu.memory[0x5000] = 0xAA
self.cpu.index = 0x5000
self.cpu.bitplane = 1
self.cpu.execute_instruction()
self.assertTrue(self.screen.get_pixel(0, 0, 1))
self.assertFalse(self.screen.get_pixel(1, 0, 1))
self.assertTrue(self.screen.get_pixel(2, 0, 1))
self.assertFalse(self.screen.get_pixel(3, 0, 1))
self.assertTrue(self.screen.get_pixel(4, 0, 1))
self.assertFalse(self.screen.get_pixel(5, 0, 1))
self.assertTrue(self.screen.get_pixel(6, 0, 1))
self.assertFalse(self.screen.get_pixel(7, 0, 1))

# Second bitplane pattern
self.assertFalse(self.screen.get_pixel(0, 0, 2))
self.assertFalse(self.screen.get_pixel(1, 0, 2))
self.assertFalse(self.screen.get_pixel(2, 0, 2))
self.assertFalse(self.screen.get_pixel(3, 0, 2))
self.assertFalse(self.screen.get_pixel(4, 0, 2))
self.assertFalse(self.screen.get_pixel(5, 0, 2))
self.assertFalse(self.screen.get_pixel(6, 0, 2))
self.assertFalse(self.screen.get_pixel(7, 0, 2))

def test_draw_sprite_normal_bitplane_2_integration_correct(self):
self.screen = Chip8Screen(2)
self.screen.init_display()
self.cpu = Chip8CPU(self.screen)

self.cpu.memory[0x0200] = 0xD0
self.cpu.memory[0x0201] = 0x01
self.cpu.memory[0x5000] = 0x55
self.cpu.index = 0x5000
self.cpu.bitplane = 2
self.cpu.execute_instruction()

# First bitplane pattern
self.assertFalse(self.screen.get_pixel(0, 0, 1))
self.assertFalse(self.screen.get_pixel(1, 0, 1))
self.assertFalse(self.screen.get_pixel(2, 0, 1))
self.assertFalse(self.screen.get_pixel(3, 0, 1))
self.assertFalse(self.screen.get_pixel(4, 0, 1))
self.assertFalse(self.screen.get_pixel(5, 0, 1))
self.assertFalse(self.screen.get_pixel(6, 0, 1))
self.assertFalse(self.screen.get_pixel(7, 0, 1))

# Second bitplane pattern
self.assertFalse(self.screen.get_pixel(0, 0, 2))
self.assertTrue(self.screen.get_pixel(1, 0, 2))
self.assertFalse(self.screen.get_pixel(2, 0, 2))
self.assertTrue(self.screen.get_pixel(3, 0, 2))
self.assertFalse(self.screen.get_pixel(4, 0, 2))
self.assertTrue(self.screen.get_pixel(5, 0, 2))
self.assertFalse(self.screen.get_pixel(6, 0, 2))
self.assertTrue(self.screen.get_pixel(7, 0, 2))

def test_draw_sprite_normal_bitplane_3_integration_correct(self):
self.screen = Chip8Screen(2)
self.screen.init_display()
self.cpu = Chip8CPU(self.screen)

self.cpu.memory[0x0200] = 0xD0
self.cpu.memory[0x0201] = 0x01
self.cpu.memory[0x5000] = 0xAA
self.cpu.memory[0x5001] = 0x55
self.cpu.bitplane = 3
self.cpu.index = 0x5000
self.cpu.execute_instruction()

# First bitplane pattern
self.assertTrue(self.screen.get_pixel(0, 0, 1))
self.assertFalse(self.screen.get_pixel(1, 0, 1))
self.assertTrue(self.screen.get_pixel(2, 0, 1))
self.assertFalse(self.screen.get_pixel(3, 0, 1))
self.assertTrue(self.screen.get_pixel(4, 0, 1))
self.assertFalse(self.screen.get_pixel(5, 0, 1))
self.assertTrue(self.screen.get_pixel(6, 0, 1))
self.assertFalse(self.screen.get_pixel(7, 0, 1))

# Second bitplane pattern
self.assertFalse(self.screen.get_pixel(0, 0, 2))
self.assertTrue(self.screen.get_pixel(1, 0, 2))
self.assertFalse(self.screen.get_pixel(2, 0, 2))
self.assertTrue(self.screen.get_pixel(3, 0, 2))
self.assertFalse(self.screen.get_pixel(4, 0, 2))
self.assertTrue(self.screen.get_pixel(5, 0, 2))
self.assertFalse(self.screen.get_pixel(6, 0, 2))
self.assertTrue(self.screen.get_pixel(7, 0, 2))

def test_draw_sprite_draws_correct_sprite(self):
screen = Chip8Screen(2)
screen.init_display()
Expand Down

0 comments on commit 7c5ce97

Please sign in to comment.