mirror of
https://github.com/KrisKennaway/ii-vision.git
synced 2024-12-31 22:30:03 +00:00
f3d03a1b87
make_edit_distance - use MASKED_DOTS since it does not have a simple relationship to the HEADER_BITS/BODY_BITS for HGR - try disabling transposition distances for Damerau-Levenshtein, this may give better quality screen - introduce separate notion of MASKED_DOTS which is the number of (coloured) pixels we can extract from MASKED_BITS. For HGR this is not the same. - fix bug in _fix_array_neighbours that was not fixing headers for HGR - don't cache everything in byte_pair_differences, it's effectively unbounded. Using 1M for LRU size seems to work just as well in practise, without leaking memory. - fix bug in _diff_weights when comparing content, we want to evaluate the effect of storing content byte in each offset separately, not cumulatively. - add a consistency check function (not currently wired up) to assert that headers/footers are in sync across columns - HGR should have 16 body bits, this was causing headers not to propagate correctly to/from neighbouring column - add test case for this bug video - Use 8 random bits consistently, using 16 in some places may have introduced bias - ignore palette bit when comparing 0x00 and 0x7f in sanity check
1134 lines
39 KiB
Python
1134 lines
39 KiB
Python
"""Tests for the screen module."""
|
|
|
|
import unittest
|
|
|
|
import numpy as np
|
|
|
|
import screen
|
|
import colours
|
|
from palette import Palette
|
|
|
|
|
|
def binary(a):
|
|
return np.vectorize("{:032b}".format)(a)
|
|
|
|
|
|
class TestDHGRBitmap(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.aux = screen.MemoryMap(screen_page=1)
|
|
self.main = screen.MemoryMap(screen_page=1)
|
|
|
|
def test_make_header(self):
|
|
"""Header extracted correctly from packed representation."""
|
|
|
|
self.assertEqual(
|
|
0b100,
|
|
screen.DHGRBitmap._make_header(
|
|
np.uint64(0b0001000011111010110000111110101000))
|
|
)
|
|
|
|
def test_make_footer(self):
|
|
"""Footer extracted correctly from packed representation."""
|
|
|
|
self.assertEqual(
|
|
0b1010000000000000000000000000000000,
|
|
screen.DHGRBitmap._make_footer(
|
|
np.uint64(0b0001000011111010110000111110101000))
|
|
)
|
|
|
|
def test_pixel_packing_offset_0(self):
|
|
"""Screen byte packing happens correctly at offset 0."""
|
|
|
|
# PBBBAAAA
|
|
self.aux.page_offset[0, 0] = 0b11110101
|
|
# PDDCCCCB
|
|
self.main.page_offset[0, 0] = 0b01000011
|
|
# PFEEEEDD
|
|
self.aux.page_offset[0, 1] = 0b11110101
|
|
# PGGGGFFF
|
|
self.main.page_offset[0, 1] = 0b01000011
|
|
|
|
dhgr = screen.DHGRBitmap(
|
|
main_memory=self.main, aux_memory=self.aux, palette=Palette.NTSC)
|
|
|
|
self.assertEqual(
|
|
0b0001000011111010110000111110101000,
|
|
dhgr.packed[0, 0]
|
|
)
|
|
|
|
# Check header on neighbouring byte
|
|
self.assertEqual(
|
|
0b0000000000000000000000000000000100,
|
|
dhgr.packed[0, 1]
|
|
)
|
|
|
|
# No other entries should be set, in particular no footer since we
|
|
# are at packed offset 0
|
|
self.assertEqual(2, np.count_nonzero(dhgr.packed))
|
|
|
|
def test_pixel_packing_offset_1(self):
|
|
"""Screen byte packing happens correctly at offset 1."""
|
|
|
|
# PBBBAAAA
|
|
self.aux.page_offset[0, 2] = 0b11110101
|
|
# PDDCCCCB
|
|
self.main.page_offset[0, 2] = 0b01000011
|
|
# PFEEEEDD
|
|
self.aux.page_offset[0, 3] = 0b11110101
|
|
# PGGGGFFF
|
|
self.main.page_offset[0, 3] = 0b01000011
|
|
|
|
dhgr = screen.DHGRBitmap(
|
|
main_memory=self.main, aux_memory=self.aux, palette=Palette.NTSC)
|
|
|
|
self.assertEqual(
|
|
0b0001000011111010110000111110101000,
|
|
dhgr.packed[0, 1]
|
|
)
|
|
|
|
# Check footer on neighbouring byte
|
|
self.assertEqual(
|
|
0b1010000000000000000000000000000000,
|
|
dhgr.packed[0, 0]
|
|
)
|
|
|
|
# Check header on neighbouring byte
|
|
self.assertEqual(
|
|
0b0000000000000000000000000000000100,
|
|
dhgr.packed[0, 2]
|
|
)
|
|
|
|
# No other entries should be set
|
|
self.assertEqual(3, np.count_nonzero(dhgr.packed))
|
|
|
|
def test_pixel_packing_offset_127(self):
|
|
"""Screen byte packing happens correctly at offset 127."""
|
|
|
|
# PBBBAAAA
|
|
self.aux.page_offset[0, 254] = 0b11110101
|
|
# PDDCCCCB
|
|
self.main.page_offset[0, 254] = 0b01000011
|
|
# PFEEEEDD
|
|
self.aux.page_offset[0, 255] = 0b11110101
|
|
# PGGGGFFF
|
|
self.main.page_offset[0, 255] = 0b01000011
|
|
|
|
dhgr = screen.DHGRBitmap(
|
|
main_memory=self.main, aux_memory=self.aux, palette=Palette.NTSC)
|
|
|
|
self.assertEqual(
|
|
0b0001000011111010110000111110101000,
|
|
dhgr.packed[0, 127]
|
|
)
|
|
|
|
# Check footer on neighbouring byte
|
|
self.assertEqual(
|
|
0b1010000000000000000000000000000000,
|
|
dhgr.packed[0, 126]
|
|
)
|
|
|
|
# No other entries should be set, in particular header should not
|
|
# propagate to next row
|
|
self.assertEqual(2, np.count_nonzero(dhgr.packed))
|
|
|
|
def test_byte_offset(self):
|
|
"""Test the byte_offset behaviour."""
|
|
|
|
self.assertEqual(0, screen.DHGRBitmap.byte_offset(0, is_aux=True))
|
|
self.assertEqual(1, screen.DHGRBitmap.byte_offset(0, is_aux=False))
|
|
self.assertEqual(2, screen.DHGRBitmap.byte_offset(1, is_aux=True))
|
|
self.assertEqual(3, screen.DHGRBitmap.byte_offset(1, is_aux=False))
|
|
|
|
def test_byte_offsets(self):
|
|
"""Test the _byte_offsets behaviour."""
|
|
|
|
self.assertEqual((0, 2), screen.DHGRBitmap._byte_offsets(is_aux=True))
|
|
self.assertEqual((1, 3), screen.DHGRBitmap._byte_offsets(is_aux=False))
|
|
|
|
def test_mask_and_shift_data(self):
|
|
"""Verify that mask_and_shift_data extracts the right bit positions."""
|
|
|
|
int13_max = np.uint64(2 ** 13 - 1)
|
|
int34_max = np.uint64(2 ** 34 - 1)
|
|
|
|
dhgr = screen.DHGRBitmap(
|
|
main_memory=self.main, aux_memory=self.aux, palette=Palette.NTSC)
|
|
|
|
for o in range(3):
|
|
self.assertEqual(
|
|
int13_max,
|
|
dhgr.mask_and_shift_data(
|
|
screen.DHGRBitmap.BYTE_MASKS[o], o
|
|
)
|
|
)
|
|
|
|
# Now check complement, i.e. no bits taken from outside expected
|
|
# range
|
|
self.assertEqual(
|
|
0,
|
|
dhgr.mask_and_shift_data(
|
|
~screen.DHGRBitmap.BYTE_MASKS[o] & int34_max, o
|
|
)
|
|
)
|
|
|
|
def test_masked_update(self):
|
|
"""Verify that masked_update updates the expected bit positions."""
|
|
|
|
self.assertEqual(
|
|
0b0000000000000000000000001111111000,
|
|
screen.DHGRBitmap.masked_update(
|
|
0, np.uint64(0), np.uint8(0xff))
|
|
)
|
|
self.assertEqual(
|
|
0b0000000000000000011111110000000000,
|
|
screen.DHGRBitmap.masked_update(
|
|
1, np.uint64(0), np.uint8(0xff))
|
|
)
|
|
self.assertEqual(
|
|
0b0000000000111111100000000000000000,
|
|
screen.DHGRBitmap.masked_update(
|
|
2, np.uint64(0), np.uint8(0xff))
|
|
)
|
|
self.assertEqual(
|
|
0b0001111111000000000000000000000000,
|
|
screen.DHGRBitmap.masked_update(
|
|
3, np.uint64(0), np.uint8(0xff))
|
|
)
|
|
|
|
# Now test masking out existing values
|
|
|
|
int34_max = np.uint64(2 ** 34 - 1)
|
|
|
|
self.assertEqual(
|
|
0b1111111111111111111111110000000111,
|
|
screen.DHGRBitmap.masked_update(0, int34_max, np.uint8(0x00))
|
|
)
|
|
self.assertEqual(
|
|
0b1111111111111111100000001111111111,
|
|
screen.DHGRBitmap.masked_update(1, int34_max, np.uint8(0x00))
|
|
)
|
|
self.assertEqual(
|
|
0b1111111111000000011111111111111111,
|
|
screen.DHGRBitmap.masked_update(2, int34_max, np.uint8(0x00))
|
|
)
|
|
self.assertEqual(
|
|
0b1110000000111111111111111111111111,
|
|
screen.DHGRBitmap.masked_update(3, int34_max, np.uint8(0x00))
|
|
)
|
|
|
|
# Test that masked_update can broadcast to numpy arrays
|
|
ary = np.zeros((2, 2), dtype=np.uint64)
|
|
|
|
elt = np.uint64(0b1111111000)
|
|
self.assertTrue(np.array_equal(
|
|
np.array([[elt, elt], [elt, elt]], dtype=np.uint64),
|
|
screen.DHGRBitmap.masked_update(0, ary, np.uint8(0xff))
|
|
))
|
|
|
|
def test_apply(self):
|
|
"""Test that apply() correctly updates neighbours."""
|
|
|
|
dhgr = screen.DHGRBitmap(
|
|
main_memory=self.main, aux_memory=self.aux, palette=Palette.NTSC)
|
|
|
|
dhgr.apply(page=0, offset=0, is_aux=True, value=np.uint8(0xff))
|
|
self.assertEqual(0b1111111000, dhgr.packed[0, 0])
|
|
|
|
dhgr.apply(page=12, offset=36, is_aux=True, value=np.uint8(0xff))
|
|
# Neighbouring header
|
|
self.assertEqual(
|
|
0,
|
|
dhgr.packed[12, 19])
|
|
# Body
|
|
self.assertEqual(
|
|
0b1111111000,
|
|
dhgr.packed[12, 18])
|
|
# Neighbouring footer
|
|
self.assertEqual(
|
|
0b1110000000000000000000000000000000,
|
|
dhgr.packed[12, 17])
|
|
|
|
# Now update the next aux offset in same uint64
|
|
dhgr.apply(page=12, offset=37, is_aux=True, value=np.uint8(0xff))
|
|
# Neighbouring header
|
|
self.assertEqual(
|
|
0,
|
|
dhgr.packed[12, 19])
|
|
# Body
|
|
self.assertEqual(
|
|
0b0000000111111100000001111111000,
|
|
dhgr.packed[12, 18]
|
|
)
|
|
# Neighbouring footer
|
|
self.assertEqual(
|
|
0b1110000000000000000000000000000000,
|
|
dhgr.packed[12, 17])
|
|
|
|
# Update offset 3, should propagate to next header
|
|
dhgr.apply(page=12, offset=37, is_aux=False, value=np.uint8(0b1010101))
|
|
self.assertEqual(
|
|
0b101,
|
|
dhgr.packed[12, 19])
|
|
self.assertEqual(
|
|
0b1010101111111100000001111111000,
|
|
dhgr.packed[12, 18]
|
|
)
|
|
self.assertEqual(
|
|
0b1110000000000000000000000000000000,
|
|
dhgr.packed[12, 17])
|
|
|
|
dhgr.apply(page=12, offset=36, is_aux=False, value=np.uint8(0b0001101))
|
|
self.assertEqual(
|
|
0b101,
|
|
dhgr.packed[12, 19])
|
|
self.assertEqual(
|
|
0b1010101111111100011011111111000,
|
|
dhgr.packed[12, 18]
|
|
)
|
|
self.assertEqual(
|
|
0b1110000000000000000000000000000000,
|
|
dhgr.packed[12, 17])
|
|
|
|
# Change offset 0, should propagate to neighbouring footer
|
|
dhgr.apply(page=12, offset=36, is_aux=True, value=np.uint8(0b0001101))
|
|
# Neighbouring header
|
|
self.assertEqual(
|
|
0b101,
|
|
dhgr.packed[12, 19])
|
|
self.assertEqual(
|
|
0b1010101111111100011010001101000,
|
|
dhgr.packed[12, 18]
|
|
)
|
|
# Neighbouring footer
|
|
self.assertEqual(
|
|
0b1010000000000000000000000000000000,
|
|
dhgr.packed[12, 17])
|
|
|
|
# Now propagate new header from neighbour onto (12, 18)
|
|
dhgr.apply(page=12, offset=35, is_aux=False, value=np.uint8(0b1010101))
|
|
self.assertEqual(
|
|
0b1010101111111100011010001101101,
|
|
dhgr.packed[12, 18]
|
|
)
|
|
# Neighbouring footer
|
|
self.assertEqual(
|
|
0b1011010101000000000000000000000000,
|
|
dhgr.packed[12, 17])
|
|
|
|
def test_fix_array_neighbours(self):
|
|
"""Test that _fix_array_neighbours DTRT after masked_update."""
|
|
|
|
dhgr = screen.DHGRBitmap(
|
|
main_memory=self.main, aux_memory=self.aux, palette=Palette.NTSC)
|
|
|
|
packed = dhgr.masked_update(0, dhgr.packed, np.uint8(0x7f))
|
|
dhgr._fix_array_neighbours(packed, 0)
|
|
|
|
# Should propagate to all footers
|
|
self.assertEqual(
|
|
0, np.count_nonzero(
|
|
packed[packed != 0b1110000000000000000000001111111000]
|
|
)
|
|
)
|
|
|
|
# Should not change headers/footers
|
|
packed = dhgr.masked_update(1, packed, np.uint8(0b1010101))
|
|
dhgr._fix_array_neighbours(packed, 1)
|
|
|
|
self.assertEqual(
|
|
0, np.count_nonzero(
|
|
packed[packed != 0b1110000000000000010101011111111000]
|
|
)
|
|
)
|
|
|
|
# Should propagate to all headers
|
|
packed = dhgr.masked_update(3, packed, np.uint8(0b0110110))
|
|
dhgr._fix_array_neighbours(packed, 3)
|
|
|
|
self.assertEqual(
|
|
0, np.count_nonzero(
|
|
packed[packed != 0b1110110110000000010101011111111011]
|
|
)
|
|
)
|
|
|
|
|
|
class TestHGRBitmap(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.main = screen.MemoryMap(screen_page=1)
|
|
|
|
def test_make_header(self):
|
|
"""Header extracted correctly from packed representation."""
|
|
|
|
self.assertEqual(
|
|
0b111,
|
|
screen.HGRBitmap._make_header(
|
|
np.uint64(0b0001100000100000000000))
|
|
)
|
|
|
|
# Now check palette bit ends up in right spot
|
|
self.assertEqual(
|
|
0b100,
|
|
screen.HGRBitmap._make_header(
|
|
np.uint64(0b0000000000100000000000))
|
|
)
|
|
|
|
def test_make_footer(self):
|
|
"""Footer extracted correctly from packed representation."""
|
|
|
|
self.assertEqual(
|
|
0b1110000000000000000000,
|
|
screen.HGRBitmap._make_footer(
|
|
np.uint64(0b0000000000010000011000))
|
|
)
|
|
|
|
# Now check palette bit ends up in right spot
|
|
self.assertEqual(
|
|
0b0010000000000000000000,
|
|
screen.HGRBitmap._make_footer(
|
|
np.uint64(0b0000000000010000000000))
|
|
)
|
|
|
|
def test_pixel_packing_p0_p0(self):
|
|
"""Screen byte packing happens correctly with P=0, P=0 palette bits."""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 0] = 0b01000011
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 1] = 0b01000011
|
|
|
|
hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
want = 0b0001000011001000011000
|
|
got = hgr.packed[0, 0]
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
def test_pixel_packing_p0_p1(self):
|
|
"""Screen byte packing happens correctly with P=0, P=1 palette bits."""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 0] = 0b01000011
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 1] = 0b11000011
|
|
|
|
hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
want = 0b0001000011101000011000
|
|
got = hgr.packed[0, 0]
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
def test_pixel_packing_p1_p0(self):
|
|
"""Screen byte packing happens correctly with P=1, P=0 palette bits."""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 0] = 0b11000011
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 1] = 0b01000011
|
|
|
|
hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
want = 0b0001000011011000011000
|
|
got = hgr.packed[0, 0]
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
def test_pixel_packing_p1_p1(self):
|
|
"""Screen byte packing happens correctly with P=1, P=1 palette bits."""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 0] = 0b11000011
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 1] = 0b11000011
|
|
|
|
hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
want = 0b1000011111000011000
|
|
got = hgr.packed[0, 0]
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
def test_apply(self):
|
|
"""Test that header, body and footer are placed correctly."""
|
|
hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
hgr.apply(0, 0, False, 0b11000011)
|
|
hgr.apply(0, 1, False, 0b11000011)
|
|
|
|
want = 0b1000011111000011000
|
|
got = hgr.packed[0, 0]
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Now check with 4 consecutive bytes, i.e. even/odd pair plus the
|
|
# neighbouring header/footer.
|
|
hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
hgr.apply(1, 197, False, 128)
|
|
hgr.apply(1, 198, False, 143)
|
|
hgr.apply(1, 199, False, 192)
|
|
hgr.apply(1, 200, False, 128)
|
|
|
|
want = 0b0011000000110001111100
|
|
got = hgr.packed[1, 199 // 2]
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
def test_double_pixels(self):
|
|
"""Verify behaviour of _double_pixels."""
|
|
|
|
want = 0b111001100110011
|
|
got = screen.HGRBitmap._double_pixels(0b1010101)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
def test_to_dots_offset_0(self):
|
|
"""Verify to_dots behaviour with byte_offset=0"""
|
|
|
|
# Header has P=0, Body has P=0
|
|
want = 0b00000000000000000111
|
|
got = screen.HGRBitmap.to_dots(0b00000000000011, 0)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=1, body has P=0 - cuts off
|
|
want = 0b00000000000000000111
|
|
got = screen.HGRBitmap.to_dots(0b00000000000111, 0)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=1, body has P=1
|
|
want = 0b00000000000000001111
|
|
got = screen.HGRBitmap.to_dots(0b00010000000111, 0)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=1, body has P=1, footer has P=0 - cuts off body
|
|
want = 0b00010011001100111111
|
|
got = screen.HGRBitmap.to_dots(0b00011010101111, 0)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=1, body has P=1, footer has P=1
|
|
want = 0b00110011001100111111
|
|
got = screen.HGRBitmap.to_dots(0b00111010101111, 0)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=1, body has P=1, footer has P=1
|
|
want = 0b100110011001100111111
|
|
got = screen.HGRBitmap.to_dots(0b10111010101111, 0)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=0, body has P=0, footer has P=1
|
|
want = 0b100000000000000000000
|
|
got = screen.HGRBitmap.to_dots(0b10100000000000, 0)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=0, body has P=0, footer has P=0
|
|
want = 0b110000000000000000000
|
|
got = screen.HGRBitmap.to_dots(0b10000000000000, 0)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
def test_to_dots_offset_1(self):
|
|
"""Verify to_dots behaviour with byte_offset=1"""
|
|
|
|
# Header has P=0, Body has P=0
|
|
want = 0b000000000000000000111
|
|
got = screen.HGRBitmap.to_dots(0b00000000000011, 1)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=1, body has P=0 - cuts off
|
|
want = 0b000000000000000000111
|
|
got = screen.HGRBitmap.to_dots(0b00000000000111, 1)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=1, body has P=1
|
|
want = 0b000000000000000001111
|
|
got = screen.HGRBitmap.to_dots(0b00000000001111, 1)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=1, body has P=1, footer has P=0 - cuts off body
|
|
want = 0b000010011001100111111
|
|
got = screen.HGRBitmap.to_dots(0b00010101011111, 1)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=1, body has P=1, footer has P=1
|
|
want = 0b000110011001100111111
|
|
got = screen.HGRBitmap.to_dots(0b00110101011111, 1)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=1, body has P=1, footer has P=1
|
|
want = 0b100110011001100111111
|
|
got = screen.HGRBitmap.to_dots(0b10110101011111, 1)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=0, body has P=0, footer has P=1
|
|
want = 0b100000000000000000000
|
|
got = screen.HGRBitmap.to_dots(0b10100000000000, 1)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
# Header has P=0, body has P=0, footer has P=0
|
|
want = 0b110000000000000000000
|
|
got = screen.HGRBitmap.to_dots(0b10000000000000, 1)
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
|
|
class TestNominalColours(unittest.TestCase):
|
|
"""Tests that screen pixel values produce expected colour sequences."""
|
|
|
|
def setUp(self) -> None:
|
|
self.main = screen.MemoryMap(screen_page=1)
|
|
|
|
self.maxDiff = None
|
|
|
|
def test_nominal_colours(self):
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 0] = 0b01010101
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 1] = 0b00101010
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 2] = 0b01010101
|
|
|
|
self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
want = 0b0100101010001010101000
|
|
got = self.hgr.packed[0, 0]
|
|
|
|
self.assertEqual(
|
|
want, got, "\n%s\n%s" % (binary(want), binary(got))
|
|
)
|
|
|
|
masked = int(screen.HGRBitmap.mask_and_shift_data(
|
|
self.hgr.packed[0, 0], byte_offset=0))
|
|
dots = screen.HGRBitmap.to_dots(masked, byte_offset=0)
|
|
self.assertEqual(
|
|
(
|
|
colours.HGRColours.MAGENTA,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
),
|
|
colours.dots_to_nominal_colour_pixels(
|
|
18, dots, colours.HGRColours,
|
|
init_phase=screen.HGRBitmap.PHASES[0])
|
|
)
|
|
|
|
# Now check byte offset 1
|
|
|
|
masked = int(screen.HGRBitmap.mask_and_shift_data(
|
|
self.hgr.packed[0, 0], byte_offset=1))
|
|
dots = screen.HGRBitmap.to_dots(masked, byte_offset=1)
|
|
self.assertEqual(
|
|
(
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET,
|
|
),
|
|
colours.dots_to_nominal_colour_pixels(
|
|
18, dots, colours.HGRColours,
|
|
init_phase=screen.HGRBitmap.PHASES[1])
|
|
)
|
|
|
|
# The following tests check for the extended/truncated behaviour across
|
|
# byte boundaries when mismatching palette bits. See Figure 8.15 from
|
|
# Sather, "Understanding the Apple IIe"
|
|
|
|
def test_nominal_colours_sather_even_1(self):
|
|
"""Extend violet into light blue."""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 0] = 0b01000000
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 1] = 0b10000000
|
|
|
|
self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
masked = int(screen.HGRBitmap.mask_and_shift_data(
|
|
self.hgr.packed[0, 0], byte_offset=0))
|
|
dots = screen.HGRBitmap.to_dots(masked, byte_offset=0)
|
|
|
|
self.assertEqual(
|
|
(
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.MAGENTA, # 1000
|
|
colours.HGRColours.VIOLET, # 1100
|
|
colours.HGRColours.LIGHT_BLUE, # 1110
|
|
colours.HGRColours.LIGHT_BLUE, # 1110
|
|
colours.HGRColours.MED_BLUE, # 0110
|
|
# last repeated bit from byte 0
|
|
colours.HGRColours.DARK_GREEN, # 0010
|
|
),
|
|
colours.dots_to_nominal_colour_pixels(
|
|
18, dots, colours.HGRColours,
|
|
init_phase=screen.HGRBitmap.PHASES[0])
|
|
)
|
|
|
|
def test_nominal_colours_sather_even_2(self):
|
|
"""Cut off blue with black to produce dark blue."""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 0] = 0b11000000
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 1] = 0b00000000
|
|
|
|
self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
masked = int(screen.HGRBitmap.mask_and_shift_data(
|
|
self.hgr.packed[0, 0], byte_offset=0))
|
|
dots = screen.HGRBitmap.to_dots(masked, byte_offset=0)
|
|
|
|
self.assertEqual(
|
|
(
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.DARK_BLUE, # 0100
|
|
colours.HGRColours.DARK_BLUE,
|
|
colours.HGRColours.DARK_BLUE,
|
|
colours.HGRColours.DARK_BLUE,
|
|
colours.HGRColours.BLACK,
|
|
),
|
|
colours.dots_to_nominal_colour_pixels(
|
|
18, dots, colours.HGRColours,
|
|
init_phase=screen.HGRBitmap.PHASES[0])
|
|
)
|
|
|
|
def test_nominal_colours_sather_even_3(self):
|
|
"""Cut off blue with green to produce aqua."""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 0] = 0b11000000
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 1] = 0b00000001
|
|
|
|
self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
masked = int(screen.HGRBitmap.mask_and_shift_data(
|
|
self.hgr.packed[0, 0], byte_offset=0))
|
|
dots = screen.HGRBitmap.to_dots(masked, byte_offset=0)
|
|
|
|
self.assertEqual(
|
|
(
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.DARK_BLUE,
|
|
colours.HGRColours.MED_BLUE,
|
|
colours.HGRColours.AQUA,
|
|
colours.HGRColours.AQUA,
|
|
colours.HGRColours.GREEN,
|
|
),
|
|
colours.dots_to_nominal_colour_pixels(
|
|
18, dots, colours.HGRColours,
|
|
init_phase=screen.HGRBitmap.PHASES[0])
|
|
)
|
|
|
|
def test_nominal_colours_sather_even_4(self):
|
|
"""Cut off white with black to produce pink."""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 0] = 0b11100000
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 1] = 0b00000000
|
|
|
|
self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
masked = int(screen.HGRBitmap.mask_and_shift_data(
|
|
self.hgr.packed[0, 0], byte_offset=0))
|
|
dots = screen.HGRBitmap.to_dots(masked, byte_offset=0)
|
|
|
|
self.assertEqual(
|
|
(
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BROWN,
|
|
colours.HGRColours.ORANGE,
|
|
colours.HGRColours.PINK,
|
|
colours.HGRColours.PINK,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.DARK_BLUE,
|
|
colours.HGRColours.BLACK,
|
|
),
|
|
colours.dots_to_nominal_colour_pixels(
|
|
18, dots, colours.HGRColours,
|
|
init_phase=screen.HGRBitmap.PHASES[0])
|
|
)
|
|
|
|
def test_nominal_colours_sather_even_5(self):
|
|
"""Cut off orange-black with green to produce bright green.
|
|
|
|
"Bright" here is because the sequence of pixels has high intensity
|
|
Orange-Orange-Yellow-Yellow-Green-Green."""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 0] = 0b10100000
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 1] = 0b00000001
|
|
|
|
self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
masked = int(screen.HGRBitmap.mask_and_shift_data(
|
|
self.hgr.packed[0, 0], byte_offset=0))
|
|
dots = screen.HGRBitmap.to_dots(masked, byte_offset=0)
|
|
|
|
self.assertEqual(
|
|
(
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BROWN, # 0001
|
|
colours.HGRColours.ORANGE, # 1001
|
|
colours.HGRColours.ORANGE, # 1001
|
|
colours.HGRColours.YELLOW, # 1011
|
|
colours.HGRColours.YELLOW, # 1011
|
|
colours.HGRColours.GREEN, # 0011
|
|
colours.HGRColours.GREEN, # 0011
|
|
),
|
|
colours.dots_to_nominal_colour_pixels(
|
|
18, dots, colours.HGRColours,
|
|
init_phase=screen.HGRBitmap.PHASES[0])
|
|
)
|
|
|
|
def test_nominal_colours_sather_odd_1(self):
|
|
"""Extend green into light brown."""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 1] = 0b01000000
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 2] = 0b10000000
|
|
|
|
self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
masked = int(screen.HGRBitmap.mask_and_shift_data(
|
|
self.hgr.packed[0, 0], byte_offset=1))
|
|
dots = screen.HGRBitmap.to_dots(masked, byte_offset=1)
|
|
|
|
self.assertEqual(
|
|
(
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.DARK_GREEN,
|
|
colours.HGRColours.GREEN,
|
|
colours.HGRColours.YELLOW,
|
|
colours.HGRColours.YELLOW,
|
|
colours.HGRColours.ORANGE,
|
|
colours.HGRColours.MAGENTA,
|
|
),
|
|
colours.dots_to_nominal_colour_pixels(
|
|
18, dots, colours.HGRColours,
|
|
init_phase=screen.HGRBitmap.PHASES[1])
|
|
)
|
|
|
|
def test_nominal_colours_sather_odd_2(self):
|
|
"""Cut off orange with black to produce dark brown."""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 1] = 0b11000000
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 2] = 0b00000000
|
|
|
|
self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
masked = int(screen.HGRBitmap.mask_and_shift_data(
|
|
self.hgr.packed[0, 0], byte_offset=1))
|
|
dots = screen.HGRBitmap.to_dots(masked, byte_offset=1)
|
|
|
|
self.assertEqual(
|
|
(
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BROWN,
|
|
colours.HGRColours.BROWN,
|
|
colours.HGRColours.BROWN,
|
|
colours.HGRColours.BROWN,
|
|
colours.HGRColours.BLACK,
|
|
),
|
|
colours.dots_to_nominal_colour_pixels(
|
|
18, dots, colours.HGRColours,
|
|
init_phase=screen.HGRBitmap.PHASES[1])
|
|
)
|
|
|
|
def test_nominal_colours_sather_odd_3(self):
|
|
"""Cut off orange with violet to produce pink."""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 1] = 0b11000000
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 2] = 0b00000001
|
|
|
|
self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
masked = int(screen.HGRBitmap.mask_and_shift_data(
|
|
self.hgr.packed[0, 0], byte_offset=1))
|
|
dots = screen.HGRBitmap.to_dots(masked, byte_offset=1)
|
|
|
|
self.assertEqual(
|
|
(
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BROWN,
|
|
colours.HGRColours.ORANGE,
|
|
colours.HGRColours.PINK,
|
|
colours.HGRColours.PINK,
|
|
colours.HGRColours.VIOLET,
|
|
),
|
|
colours.dots_to_nominal_colour_pixels(
|
|
18, dots, colours.HGRColours,
|
|
init_phase=screen.HGRBitmap.PHASES[1])
|
|
)
|
|
|
|
def test_nominal_colours_sather_odd_4(self):
|
|
"""Cut off white with black to produce aqua."""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 1] = 0b11100000
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 2] = 0b00000000
|
|
|
|
self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
masked = int(screen.HGRBitmap.mask_and_shift_data(
|
|
self.hgr.packed[0, 0], byte_offset=1))
|
|
dots = screen.HGRBitmap.to_dots(masked, byte_offset=1)
|
|
|
|
self.assertEqual(
|
|
(
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.DARK_BLUE,
|
|
colours.HGRColours.MED_BLUE,
|
|
colours.HGRColours.AQUA,
|
|
colours.HGRColours.AQUA,
|
|
colours.HGRColours.GREEN,
|
|
colours.HGRColours.BROWN,
|
|
colours.HGRColours.BLACK,
|
|
),
|
|
colours.dots_to_nominal_colour_pixels(
|
|
18, dots, colours.HGRColours,
|
|
init_phase=screen.HGRBitmap.PHASES[1])
|
|
)
|
|
|
|
def test_nominal_colours_sather_odd_5(self):
|
|
"""Cut off blue-black with violet to produce bright violet.
|
|
|
|
"Bright" here is because the sequence of pixels has high intensity
|
|
Blue-Blue-Light Blue-Light Blue-Violet-Violet.
|
|
"""
|
|
|
|
# PDCCBBAA
|
|
self.main.page_offset[0, 1] = 0b10100000
|
|
# PGGFFEED
|
|
self.main.page_offset[0, 2] = 0b00000001
|
|
|
|
self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)
|
|
|
|
masked = int(screen.HGRBitmap.mask_and_shift_data(
|
|
self.hgr.packed[0, 0], byte_offset=1))
|
|
dots = screen.HGRBitmap.to_dots(masked, byte_offset=1)
|
|
|
|
self.assertEqual(
|
|
(
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.BLACK,
|
|
colours.HGRColours.DARK_BLUE,
|
|
colours.HGRColours.MED_BLUE,
|
|
colours.HGRColours.MED_BLUE,
|
|
colours.HGRColours.LIGHT_BLUE,
|
|
colours.HGRColours.LIGHT_BLUE,
|
|
colours.HGRColours.VIOLET,
|
|
colours.HGRColours.VIOLET
|
|
),
|
|
colours.dots_to_nominal_colour_pixels(
|
|
18, dots, colours.HGRColours,
|
|
init_phase=screen.HGRBitmap.PHASES[1])
|
|
)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|