Three blocks move and nothing else changes. Branches take 0x60, subroutines take 0x70, and the ALU moves up into the 0x10 block the two of them used to share. Order within each block is preserved exactly - this relocates them, it does not rethink them. WHAT IT BUYS IS AN EMPTY 0x00 TO 0x0F. Program Memory that was never written, or a load that stopped part way and left zeroes in its tail, used to read as a long run of ADDs: the machine carried on through them, arrived somewhere unpredictable, and whatever broke there was a long way from the byte that caused it. Now it faults where it is met: Fault: 0x00 at Program Address 0x0004 is not an instruction. That is the address of the byte after the last real instruction, which is the difference between a diagnosis and a search. Reserving the whole nibble rather than just 0x00 means a run into blank memory faults wherever it starts rather than only when it lands on the right byte. runOffTest records it, and the block is left empty for whatever turns out to want it. The other half is room: branches and subroutines had filled 0x10 to 0x1F between them, so a service return that keeps Q and DP3 had nowhere to sit next to its family. It has 0x76 waiting now. Five places wrote an opcode down that the scripted remap did not reach, and four of them were found by tests rather than by looking: - secondPass.c lists which opcodes take an address, and firstPass.c knows SWI by number. Missing those made XOR read as a branch. - Asm.asm knows SWI by number too, being the other assembler. Missing it made the native and host assemblers disagree byte for byte, which is exactly the check that exists to catch a thing known in two places. - loaderTest.asm carries a hand written payload, and its RETI was 0x19. To the assembler those are numbers and to the program they are data, so nothing but running it could notice. It says so in a comment now. - The Assembler Manual prints the bytes hello.asm assembles to, and two of them were branches. The monitor's recorded disassembly moved by exactly the bytes it should: 18 became 72 wherever SWI appears, with SETD and INIB untouched and every disassembled line still reading the same.
63 lines
2.2 KiB
Python
Executable File
63 lines
2.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""The instruction table, as the assembler has it.
|
|
|
|
The monitor needs the same 70 instructions the assembler does, with the same names and the
|
|
same lengths, and a disassembler that disagreed with the assembler about how long an
|
|
instruction is would not merely print one thing wrong - it would lose its place and print
|
|
everything after it wrong too. So the table is generated from assembly.c rather than typed
|
|
out again, and Tests/docs.sh checks the generated form against what is in the monitor.
|
|
|
|
Shapes are what follows the opcode:
|
|
0 nothing 1 an address 2 one byte 3 a Data Pointer selector
|
|
4 selector, byte 5 selector, address 6 two selectors
|
|
"""
|
|
import re
|
|
import sys
|
|
|
|
# Branches and the two calls that carry an address: the ten in 0x6X, plus RCAL and CALL.
|
|
ADDRESS = {0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71}
|
|
ONE_BYTE = {0x72, 0x26, 0x27}
|
|
TWO_SELECTORS = {0x4A, 0x4B}
|
|
SELECTOR = {0x65, 0x33, 0x36, 0x40, 0x41, 0x42, 0x43, 0x44,
|
|
0x45, 0x46, 0x47, 0x48, 0x49, 0x4C, 0x4D,
|
|
0x4E, 0x4F, 0x50, 0x51}
|
|
|
|
|
|
def shapeOf(opcode):
|
|
if opcode in TWO_SELECTORS:
|
|
return 6
|
|
if opcode == 0x47: # SETD, a selector and then an address
|
|
return 5
|
|
if opcode in (0x48, 0x49): # DPUP and DPDN, a selector and then a byte
|
|
return 4
|
|
if opcode in SELECTOR:
|
|
return 3
|
|
if opcode in ADDRESS:
|
|
return 1
|
|
if opcode in ONE_BYTE or (opcode & 0xF0) in (0xD0, 0xE0):
|
|
return 2
|
|
return 0
|
|
|
|
|
|
def table(path="Source/Assembler/assembly.c"):
|
|
source = open(path).read()
|
|
found = re.findall(r'\{0x([0-9A-Fa-f]{2}),\s*"([A-Z0-9]+)"\}', source)
|
|
return [(int(code, 16), name) for code, name in found]
|
|
|
|
|
|
def asAssembly(entries):
|
|
lines = []
|
|
for opcode, name in entries:
|
|
padded = (name + " ")[:4]
|
|
lines.append(' 0x%02X 0d%d "%s"' % (opcode, shapeOf(opcode), padded))
|
|
return lines
|
|
|
|
|
|
if __name__ == "__main__":
|
|
entries = table()
|
|
if len(sys.argv) > 1 and sys.argv[1] == "--count":
|
|
print(len(entries))
|
|
else:
|
|
for line in asAssembly(entries):
|
|
print(line)
|