Skip to content

Commit

Permalink
Enable sna2skool.py to disassemble LD (nn),HL and LD HL,(nn) variants
Browse files Browse the repository at this point in the history
  • Loading branch information
skoolkid committed Jun 11, 2024
1 parent a2e1379 commit 34b35dd
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 11 deletions.
11 changes: 9 additions & 2 deletions skoolkit/disassembler.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,15 @@ def __init__(self, snapshot, config):
self.defs = 'DEFS '
self.defw = 'DEFW '
self.create_opcodes()
for opcode in [e.strip() for e in config.opcodes.upper().split(',')]:
if opcode == 'ED70':
opcodes = [e.strip() for e in config.opcodes.upper().split(',')]
if 'ALL' in opcodes:
opcodes = 'ED63,ED6B,ED70,ED71,IM,NEG,RETN,XYCB'.split(',')
for opcode in opcodes:
if opcode == 'ED63':
self.after_ED[0x63] = (self.word_arg, 'LD ({}),HL')
elif opcode == 'ED6B':
self.after_ED[0x6B] = (self.word_arg, 'LD HL,({})')
elif opcode == 'ED70':
self.after_ED[0x70] = (self.no_arg, 'IN F,(C)')
elif opcode == 'ED71':
self.after_ED[0x71] = (self.no_arg, 'OUT (C),0')
Expand Down
3 changes: 3 additions & 0 deletions sphinx/source/commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1146,12 +1146,15 @@ configuration parameters are:
The ``Opcodes`` list is empty by default, but may contain any of the following
values:

* ``ED63`` - LD (nn),HL (4-byte variant)
* ``ED6B`` - LD HL,(nn) (4-byte variant)
* ``ED70`` - IN F,(C)
* ``ED71`` - OUT (C),0
* ``IM`` - IM 0/1/2 variants (ED followed by 4E/66/6E/76/7E)
* ``NEG`` - NEG variants (ED followed by 4C/54/5C/64/6C/74/7C)
* ``RETN`` - RETN variants (ED followed by 55/5D/65/6D/75/7D)
* ``XYCB`` - undocumented instructions with DDCB or FDCB opcode prefixes
* ``ALL`` - all of the above

Note that if your skool file contains any non-standard instructions (such as
'IN F,(C)') or instructions that derive from non-standard opcode sequences
Expand Down
17 changes: 10 additions & 7 deletions sphinx/source/man/sna2skool.py.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ configuration parameters are:
:ListRefs: When to add a comment that lists routine or entry point referrers:
never (``0``), if no other comment is defined at the entry point (``1``,
the default), or always (``2``).
:Opcodes: Comma-separated list of additional opcode sequences to disassemble
(see below).
:Opcodes: Comma-separated list of values specifying additional opcode
sequences to disassemble (see below).
:Ref: Template used to format the comment for a routine with exactly one
referrer (default: ``Used by the routine at {ref}.``).
:RefFormat: Template used to format referrers in the ``{ref}`` and ``{refs}``
Expand Down Expand Up @@ -129,15 +129,18 @@ configuration parameters are:
or don't (``0``, the default).

The ``Opcodes`` list is empty by default, but may contain any of the following
hexadecimal opcode sequences:
values:

|
| ``ED63`` - LD (nn),HL (4-byte variant)
| ``ED6B`` - LD HL,(nn) (4-byte variant)
| ``ED70`` - IN F,(C)
| ``ED71`` - OUT (C),0
Note that assemblers may not recognise these instructions, so if your skool
file contains any of them, care must be taken when using an assembler on the
output of ``skool2asm.py``.
| ``IM`` - IM 0/1/2 variants (ED followed by 4E/66/6E/76/7E)
| ``NEG`` - NEG variants (ED followed by 4C/54/5C/64/6C/74/7C)
| ``RETN`` - RETN variants (ED followed by 55/5D/65/6D/75/7D)
| ``XYCB`` - undocumented instructions with DDCB or FDCB opcode prefixes
| ``ALL`` - all of the above
Configuration parameters must appear in a ``[sna2skool]`` section. For example,
to make ``sna2skool.py`` generate hexadecimal skool files with a line width of
Expand Down
48 changes: 48 additions & 0 deletions tests/test_disassembler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1803,6 +1803,8 @@
}

ADDITIONAL_OPCODES_ED = {
'ED630180': ('ED63', 'LD (32769),HL'),
'ED6BFEFF': ('ED6B', 'LD HL,(65534)'),
'ED70': ('ED70', 'IN F,(C)'),
'ED71': ('ED71', 'OUT (C),0'),
}
Expand Down Expand Up @@ -2354,6 +2356,16 @@ def test_additional_opcodes_ed(self):
self.assertEqual(len(instructions), 1)
self.assertEqual(instructions[0][1], op)

def test_additional_opcodes_ed_all(self):
snapshot = [0] * 4
disassembler = self._get_disassembler(snapshot, opcodes='ALL')
for hex_bytes, (opcodes, op) in ADDITIONAL_OPCODES_ED.items():
end = len(hex_bytes) // 2
snapshot[0:end] = [int(hex_bytes[i:i + 2], 16) for i in range(0, len(hex_bytes), 2)]
instructions = disassembler.disassemble(0, end, 'n')
self.assertEqual(len(instructions), 1)
self.assertEqual(instructions[0][1], op)

def test_additional_opcodes_im(self):
snapshot = [0xED, 0, 0, 0]
disassembler = self._get_disassembler(snapshot, opcodes='IM')
Expand All @@ -2363,6 +2375,15 @@ def test_additional_opcodes_im(self):
self.assertEqual(len(instructions), 1)
self.assertEqual(instructions[0][1], f'IM {mode}')

def test_additional_opcodes_im_all(self):
snapshot = [0xED, 0, 0, 0]
disassembler = self._get_disassembler(snapshot, opcodes='ALL')
for opcode, mode in ((0x4E, 0), (0x66, 0), (0x6E, 0), (0x76, 1), (0x7E, 2)):
snapshot[1] = opcode
instructions = disassembler.disassemble(0, 2, 'n')
self.assertEqual(len(instructions), 1)
self.assertEqual(instructions[0][1], f'IM {mode}')

def test_additional_opcodes_neg(self):
snapshot = [0xED, 0, 0, 0]
disassembler = self._get_disassembler(snapshot, opcodes='NEG')
Expand All @@ -2372,6 +2393,15 @@ def test_additional_opcodes_neg(self):
self.assertEqual(len(instructions), 1)
self.assertEqual(instructions[0][1], 'NEG')

def test_additional_opcodes_neg_all(self):
snapshot = [0xED, 0, 0, 0]
disassembler = self._get_disassembler(snapshot, opcodes='ALL')
for opcode in range(0x4C, 0x7D, 8):
snapshot[1] = opcode
instructions = disassembler.disassemble(0, 2, 'n')
self.assertEqual(len(instructions), 1)
self.assertEqual(instructions[0][1], 'NEG')

def test_additional_opcodes_retn(self):
snapshot = [0xED, 0, 0, 0]
disassembler = self._get_disassembler(snapshot, opcodes='RETN')
Expand All @@ -2381,6 +2411,15 @@ def test_additional_opcodes_retn(self):
self.assertEqual(len(instructions), 1)
self.assertEqual(instructions[0][1], 'RETN')

def test_additional_opcodes_retn_all(self):
snapshot = [0xED, 0, 0, 0]
disassembler = self._get_disassembler(snapshot, opcodes='ALL')
for opcode in range(0x55, 0x7E, 8):
snapshot[1] = opcode
instructions = disassembler.disassemble(0, 2, 'n')
self.assertEqual(len(instructions), 1)
self.assertEqual(instructions[0][1], 'RETN')

def test_additional_opcodes_xycb(self):
snapshot = [0] * 4
disassembler = self._get_disassembler(snapshot, opcodes='XYCB')
Expand All @@ -2390,6 +2429,15 @@ def test_additional_opcodes_xycb(self):
self.assertEqual(len(instructions), 1)
self.assertEqual(instructions[0][1], op)

def test_additional_opcodes_xycb_all(self):
snapshot = [0] * 4
disassembler = self._get_disassembler(snapshot, opcodes='ALL')
for hex_bytes, op in ADDITIONAL_OPCODES_XYCB.items():
snapshot[0:4] = [int(hex_bytes[i:i + 2], 16) for i in range(0, 8, 2)]
instructions = disassembler.disassemble(0, 4, 'n')
self.assertEqual(len(instructions), 1)
self.assertEqual(instructions[0][1], op)

def test_boundary_asm(self):
for start, data, op in BOUNDARY_ASM:
instructions = self._get_instructions(start, data)
Expand Down
42 changes: 40 additions & 2 deletions tests/test_snaskool.py
Original file line number Diff line number Diff line change
Expand Up @@ -3415,6 +3415,40 @@ def test_instruction_width_large(self):
self._test_write_skool(snapshot, ctl, exp_skool, params={'InstructionWidth': 17})

def test_opcodes(self):
snapshot = [
0xED, 0x70, # IN F,(C)
0xED, 0x71, # OUT (C),0
0xDD, 0xCB, 0x00, 0x3F, # SRL (IX+0),A
0xFD, 0xCB, 0x00, 0x80, # RES 0,(IY+0),B
0xED, 0x7C, # NEG
0xED, 0x7D, # RETN
0xED, 0x66, # IM 0
0xED, 0x76, # IM 1
0xED, 0x7E, # IM 2
0xED, 0x63, 0x01, 0x80, # LD (32769),HL
0xED, 0x6B, 0x01, 0xC0, # LD HL,(49153)
]
ctl = """
c 00000
i 00030
"""
exp_skool = """
; Routine at 0
c00000 IN F,(C) ;
00002 OUT (C),0 ;
00004 SRL (IX+0),A ;
00008 RES 0,(IY+0),B ;
00012 NEG ;
00014 RETN ;
00016 IM 0 ;
00018 IM 1 ;
00020 IM 2 ;
00022 LD (32769),HL ;
00026 LD HL,(49153) ;
"""
self._test_write_skool(snapshot, ctl, exp_skool, params={'Opcodes': 'ED63,ED6B,ED70,ED71,IM,NEG,RETN,XYCB'})

def test_opcodes_all(self):
snapshot = [
0xED, 0x70, # IN F,(C)
0xED, 0x71, # OUT (C),0
Expand All @@ -3425,10 +3459,12 @@ def test_opcodes(self):
0xED, 0x6E, # IM 0
0xED, 0x76, # IM 1
0xED, 0x7E, # IM 2
0xED, 0x63, 0x00, 0x80, # LD (32768),HL
0xED, 0x6B, 0x00, 0xC0, # LD HL,(49152)
]
ctl = """
c 00000
i 00022
i 00030
"""
exp_skool = """
; Routine at 0
Expand All @@ -3441,8 +3477,10 @@ def test_opcodes(self):
00016 IM 0 ;
00018 IM 1 ;
00020 IM 2 ;
00022 LD (32768),HL ;
00026 LD HL,(49152) ;
"""
self._test_write_skool(snapshot, ctl, exp_skool, params={'Opcodes': 'ED70,ED71,IM,NEG,RETN,XYCB'})
self._test_write_skool(snapshot, ctl, exp_skool, params={'Opcodes': 'ALL'})

def test_semicolons_bcgi(self):
snapshot = [0, 201, 0, 0, 0, 65, 0, 0, 0]
Expand Down

0 comments on commit 34b35dd

Please sign in to comment.