Skip to content

Commit 90a6638

Browse files
committed
tests: add test for outfmt
1 parent 2b6fecc commit 90a6638

6 files changed

Lines changed: 281 additions & 0 deletions

File tree

‎tests/outfmt/__init__.py‎

Whitespace-only changes.

‎tests/outfmt/test_binary.py‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# --------------------------------------------------------------------
2+
# SPDX-License-Identifier: AGPL-3.0-or-later
3+
# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors.
4+
# See the file CONTRIBUTORS.md for copyright details.
5+
# See https://www.gnu.org/licenses/agpl-3.0.html for details.
6+
# --------------------------------------------------------------------
7+
8+
from src.outfmt.binary import BinaryEmitter
9+
10+
11+
def test_binary_emitter(tmp_path):
12+
output_file = tmp_path / "test.bin"
13+
emitter = BinaryEmitter()
14+
program_bytes = b"\x01\x02\x03\x04"
15+
16+
emitter.emit(
17+
output_filename=str(output_file),
18+
program_name="test",
19+
loader_bytes=None,
20+
entry_point=16384,
21+
program_bytes=program_bytes,
22+
aux_bin_blocks=[],
23+
aux_headless_bin_blocks=[],
24+
)
25+
26+
assert output_file.exists()
27+
assert output_file.read_bytes() == program_bytes

‎tests/outfmt/test_sna.py‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# --------------------------------------------------------------------
2+
# SPDX-License-Identifier: AGPL-3.0-or-later
3+
# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors.
4+
# See the file CONTRIBUTORS.md for copyright details.
5+
# See https://www.gnu.org/licenses/agpl-3.0.html for details.
6+
# --------------------------------------------------------------------
7+
8+
from src.outfmt.sna import SnaEmitter
9+
10+
11+
def test_sna_emitter_generate():
12+
emitter = SnaEmitter()
13+
program_bytes = b"\x00" * 100
14+
entry_point = 0x8000
15+
16+
# generate(self, loader_bytes, clear_addr, entry_point, program_bytes)
17+
# SnaEmitter.emit calls generate(None, entry_point - 1, entry_point, program_bytes)
18+
sna_data = emitter.generate(None, entry_point - 1, entry_point, program_bytes)
19+
20+
assert len(sna_data) == 27 + 49152
21+
22+
# Check some header values
23+
# snapshot.I is initialized to 0x3F in GenSnapshot
24+
assert sna_data[0] == 0x3F
25+
26+
# Border color (snapshot.outFE & 7)
27+
# GenSnapshot.outFE = 0x0F, so 0x0F & 7 = 7
28+
assert sna_data[26] == 7
29+
30+
# Check SP
31+
# GenSnapshot: SP = clear_addr - 3 = (0x8000 - 1) - 3 = 0x7FFC = 32764
32+
# SnaEmitter: SP = snapshot.SP - 2 = 32764 - 2 = 32762 (0x7FFA)
33+
# sna_data[23] = 0xFA, sna_data[24] = 0x7F
34+
assert sna_data[23] == 0xFA
35+
assert sna_data[24] == 0x7F
36+
37+
# Check PC patched on stack
38+
# snapshot.PCL = 0x9E, snapshot.PCH = 0x1B
39+
# Index in sna_data = 27 + 32762 - 16384 = 16405
40+
assert sna_data[27 + 32762 - 16384] == 0x9E
41+
assert sna_data[27 + 32762 - 16384 + 1] == 0x1B
42+
43+
44+
def test_sna_emitter_emit(tmp_path):
45+
output_file = tmp_path / "test.sna"
46+
emitter = SnaEmitter()
47+
emitter.emit(
48+
output_filename=str(output_file),
49+
program_name="test",
50+
loader_bytes=None,
51+
entry_point=0x8000,
52+
program_bytes=b"\x00" * 100,
53+
aux_bin_blocks=[],
54+
aux_headless_bin_blocks=[],
55+
)
56+
assert output_file.exists()
57+
assert len(output_file.read_bytes()) == 27 + 49152

‎tests/outfmt/test_tap.py‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# --------------------------------------------------------------------
2+
# SPDX-License-Identifier: AGPL-3.0-or-later
3+
# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors.
4+
# See the file CONTRIBUTORS.md for copyright details.
5+
# See https://www.gnu.org/licenses/agpl-3.0.html for details.
6+
# --------------------------------------------------------------------
7+
8+
from src.outfmt.tap import TAP
9+
10+
11+
def test_tap_init():
12+
tap = TAP()
13+
assert tap.output == b""
14+
15+
16+
def test_tap_standard_block():
17+
tap = TAP()
18+
tap.standard_block(b"\x01\x02")
19+
# Length (3 -> [0x03, 0x00])
20+
# Data: \x01\x02
21+
# Checksum: 0x01 ^ 0x02 = 0x03
22+
assert tap.output == b"\x03\x00\x01\x02\x03"
23+
24+
25+
def test_tap_emit(tmp_path):
26+
output_file = tmp_path / "test.tap"
27+
tap = TAP()
28+
program_bytes = b"\x00\x01"
29+
tap.emit(
30+
output_filename=str(output_file),
31+
program_name="test",
32+
loader_bytes=None,
33+
entry_point=16384,
34+
program_bytes=program_bytes,
35+
aux_bin_blocks=[],
36+
aux_headless_bin_blocks=[],
37+
)
38+
assert output_file.exists()
39+
content = output_file.read_bytes()
40+
41+
# Header block
42+
# 0x13 0x00 (Length)
43+
# 0x00 (BLOCK_TYPE_HEADER)
44+
# 0x03 (HEADER_TYPE_CODE)
45+
# "test " (10 bytes)
46+
# 0x02 0x00 (Length 2)
47+
# 0x00 0x40 (Address 16384)
48+
# 0x00 0x80 (32768)
49+
# Checksum
50+
expected_header_content = b"\x00\x03test \x02\x00\x00\x40\x00\x80"
51+
checksum = 0
52+
for b in expected_header_content:
53+
checksum ^= b
54+
expected_header = b"\x13\x00" + expected_header_content + bytes([checksum])
55+
assert content.startswith(expected_header)
56+
57+
# Data block
58+
# 0x04 0x00 (Length)
59+
# 0xFF (BLOCK_TYPE_DATA)
60+
# 0x00 0x01 (data)
61+
# Checksum: 0xFF ^ 0x00 ^ 0x01 = 0xFE
62+
expected_data = b"\x04\x00\xff\x00\x01\xfe"
63+
assert expected_data in content

‎tests/outfmt/test_tzx.py‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# --------------------------------------------------------------------
2+
# SPDX-License-Identifier: AGPL-3.0-or-later
3+
# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors.
4+
# See the file CONTRIBUTORS.md for copyright details.
5+
# See https://www.gnu.org/licenses/agpl-3.0.html for details.
6+
# --------------------------------------------------------------------
7+
8+
from src.outfmt.tzx import TZX
9+
10+
11+
def test_tzx_init():
12+
tzx = TZX()
13+
assert tzx.output == b"ZXTape!\x1a\x01\x15"
14+
15+
16+
def test_tzx_lh():
17+
tzx = TZX()
18+
assert tzx.LH(0x1234) == [0x34, 0x12]
19+
assert tzx.LH(0x00FF) == [0xFF, 0x00]
20+
21+
22+
def test_tzx_standard_block():
23+
tzx = TZX()
24+
tzx.output = bytearray()
25+
tzx.standard_block(b"\x01\x02")
26+
# BLOCK_STANDARD (0x10)
27+
# Pause (1000ms -> [0xE8, 0x03])
28+
# Length (3 -> [0x03, 0x00]) - length of data + 1 for checksum
29+
# Data: \x01\x02
30+
# Checksum: 0x01 ^ 0x02 = 0x03
31+
assert tzx.output == b"\x10\xe8\x03\x03\x00\x01\x02\x03"
32+
33+
34+
def test_tzx_emit(tmp_path):
35+
output_file = tmp_path / "test.tzx"
36+
tzx = TZX()
37+
program_bytes = b"\x00\x01"
38+
tzx.emit(
39+
output_filename=str(output_file),
40+
program_name="test",
41+
loader_bytes=None,
42+
entry_point=16384,
43+
program_bytes=program_bytes,
44+
aux_bin_blocks=[],
45+
aux_headless_bin_blocks=[],
46+
)
47+
assert output_file.exists()
48+
content = output_file.read_bytes()
49+
assert content.startswith(b"ZXTape!\x1a\x01\x15")
50+
51+
# Header block for "test"
52+
# 0x10 (Standard block ID)
53+
# 0xE8 0x03 (1000ms pause)
54+
# 0x13 0x00 (Length of header: 1 + 1 + 10 + 2 + 2 + 2 + 1 = 19 -> 0x13)
55+
# 0x00 (BLOCK_TYPE_HEADER)
56+
# 0x03 (HEADER_TYPE_CODE)
57+
# "test " (10 bytes)
58+
# 0x02 0x00 (Length 2)
59+
# 0x00 0x40 (Address 16384)
60+
# 0x00 0x80 (32768)
61+
# Checksum (XOR of all bytes in block)
62+
expected_header_content = b"\x00\x03test \x02\x00\x00\x40\x00\x80"
63+
checksum = 0
64+
for b in expected_header_content:
65+
checksum ^= b
66+
expected_header_block = b"\x10\xe8\x03\x13\x00" + expected_header_content + bytes([checksum])
67+
assert expected_header_block in content
68+
69+
# Data block
70+
# 0x10
71+
# 0xE8 0x03
72+
# 0x04 0x00 (Length of data: 1 (type) + 2 (bytes) + 1 (checksum) = 4)
73+
# 0xFF (BLOCK_TYPE_DATA)
74+
# 0x00 0x01 (data)
75+
# Checksum: 0xFF ^ 0x00 ^ 0x01 = 0xFE
76+
expected_data_block = b"\x10\xe8\x03\x04\x00\xff\x00\x01\xfe"
77+
assert expected_data_block in content

‎tests/outfmt/test_z80.py‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# --------------------------------------------------------------------
2+
# SPDX-License-Identifier: AGPL-3.0-or-later
3+
# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors.
4+
# See the file CONTRIBUTORS.md for copyright details.
5+
# See https://www.gnu.org/licenses/agpl-3.0.html for details.
6+
# --------------------------------------------------------------------
7+
8+
from src.outfmt.z80 import Z80Emitter
9+
10+
11+
def test_z80_emitter_generate():
12+
emitter = Z80Emitter()
13+
program_bytes = b"\x00" * 100
14+
entry_point = 0x8000
15+
16+
# generate(self, loader_bytes, clear_addr, entry_point, program_bytes)
17+
z80_data = emitter.generate(None, entry_point - 1, entry_point, program_bytes)
18+
19+
# Header is 30 bytes
20+
assert len(z80_data) > 30
21+
assert z80_data[10] == 0x3F # snapshot.I
22+
23+
# PC in Z80 version 1 header at offset 6
24+
# GenSnapshot default PC is 0x1B9E -> PCL=0x9E, PCH=0x1B
25+
assert z80_data[6] == 0x9E
26+
assert z80_data[7] == 0x1B
27+
28+
# End marker
29+
assert z80_data.endswith(b"\x00\xed\xed\x00")
30+
31+
32+
def test_z80_compression():
33+
emitter = Z80Emitter()
34+
35+
# program_bytes with 4 EDs to test compression
36+
program_bytes = b"\xed\xed\xed\xed"
37+
entry_point = 0x8000
38+
39+
z80_data = emitter.generate(None, entry_point - 1, entry_point, program_bytes)
40+
41+
# The 4 EDs should be compressed to ED ED 04 ED
42+
assert b"\xed\xed\x04\xed" in z80_data
43+
44+
45+
def test_z80_emitter_emit(tmp_path):
46+
output_file = tmp_path / "test.z80"
47+
emitter = Z80Emitter()
48+
emitter.emit(
49+
output_filename=str(output_file),
50+
program_name="test",
51+
loader_bytes=None,
52+
entry_point=0x8000,
53+
program_bytes=b"\x00" * 100,
54+
aux_bin_blocks=[],
55+
aux_headless_bin_blocks=[],
56+
)
57+
assert output_file.exists()

0 commit comments

Comments
 (0)