APX Pascal Architecture, part four
The last blog entry introduced the tools I'm using to explore the Pascal runtime, and included a preliminary (i.e. rough) disassembly. Now we'll start refining that disassembly and start discussing more of the opcodes.
Firstly, the last listing was erroneous around $B959 to $B991. There are strings there I somehow missed when spot checking the disassembly, so I've fixed up that part of the disassembly. There were also a couple of missing $9B's as well after strings, and the p-code disassembly had a couple of errors as well which I've now fixed.
Now let's discuss some more opcodes. The simplest opcode in the listing is opcode DB. It is just:
AF9D: E8 INX AF9E: E8 INX AF9F: 4C 9D 00 JMP NEXT_OP1
Since X is the current evaluation stack pointer, and it grows downwards, this opcode drops the topmost entry of the stack, so let's call it DROP.
Another simple opcode is $DA, which disassembles as:
AF8C: CA DEX AF8D: CA DEX AF8E: BD 03 06 LDA EVALPAGE+3,X AF91: 9D 01 06 STA EVALPAGE+1,X AF94: BD 02 06 LDA EVALPAGE+2,X AF97: 9D 00 06 STA EVALPAGE,X AF9A: 4C 9D 00 JMP NEXT_OP1
This adds one entry to the stack, and copies the (previous) top element to it, so we can call this DUP.
Opcode D2 is a bit longer, but just involves moving things around the stack, so that the first two elements are exchanged, so let's call it SWAP.
AF5F: BC 00 06 LDY EVALPAGE,X AF62: BD 02 06 LDA EVALPAGE+2,X AF65: 9D 00 06 STA EVALPAGE,X AF68: 98 TYA AF69: 9D 02 06 STA EVALPAGE+2,X AF6C: BC 01 06 LDY EVALPAGE+1,X AF6F: BD 03 06 LDA EVALPAGE+3,X AF72: 9D 01 06 STA EVALPAGE+1,X AF75: 98 TYA AF76: 9D 03 06 STA EVALPAGE+3,X AF79: 4C 9D 00 JMP NEXT_OP1
Some other simple stack-only opcodes are 30 (AND), 32 (OR), 34 (NOT), 36 (EOR), 38 (NEG), 40 (ADD) and 44 (SUB). All of these replace the top two values on the stack with the result of the operation.
Opcodes 60 and 70 oddly point to the same code, which looks like this:
B185: BD 01 06 LDA EVALPAGE+1,X B188: DD 03 06 CMP EVALPAGE+3,X B18B: D0 5C BNE $B1E9 B18D: BD 00 06 LDA EVALPAGE,X B190: DD 02 06 CMP EVALPAGE+2,X B193: D0 54 BNE $B1E9 B195: F0 5F BEQ $B1F6 ... B1E9: E8 INX B1EA: E8 INX B1EB: A9 00 LDA #0 B1ED: 9D 00 06 STA EVALPAGE,X B1F0: 9D 01 06 STA EVALPAGE+1,X B1F3: 4C 9D 00 JMP NEXT_OP1 B1F6: E8 INX B1F7: E8 INX B1F8: A9 01 LDA #1 B1FA: 9D 00 06 STA EVALPAGE,X B1FD: A9 00 LDA #0 B1FF: 9D 01 06 STA EVALPAGE+1,X B202: 4C 9D 00 JMP NEXT_OP1
If the top two values are equal, we replace them with a 1, otherwise we replace them with a 0. So let's call them EQU. Opcodes 62 and 72 reverses this, so let's call them NEQ. Now why are there two equivalent opcodes? Well, let's look at opcode 64 and 74. 64 is simply:
B1A9: 20 2F BE JSR $BE2F B1AC: F0 3B BEQ $B1E9 B1AE: 30 39 BMI $B1E9 B1B0: 10 44 BPL $B1F6
and 74 is similar:
B1C9: 20 2F BE JSR $BE2F B1CC: F0 1B BEQ $B1E9 B1CE: 90 19 BCC $B1E9 B1D0: B0 24 BCS $B1F6
with
BE2F: BD 02 06 LDA EVALPAGE+2,X BE32: DD 00 06 CMP EVALPAGE,X BE35: F0 0B BEQ $BE42 BE37: BD 03 06 LDA EVALPAGE+3,X BE3A: FD 01 06 SBC EVALPAGE+1,X BE3D: 09 01 ORA #1 BE3F: 70 0A BVS $BE4B BE41: 60 RTS BE42: BD 03 06 LDA EVALPAGE+3,X BE45: FD 01 06 SBC EVALPAGE+1,X BE48: 70 01 BVS $BE4B BE4A: 60 RTS BE4B: 49 80 EOR #$80 BE4D: 09 01 ORA #1 BE4F: 60 RTS
This difference here seems to be whether the 16-bit comparisons here are done signed or unsigned. The 6x opcodes are signed comparisons, and the 7x opcodes are unsigned comparisons. 60 is EQU and 70 is UEQU, which happen to have identical implementations, and 62 and 72 are similarly NEQ and UNEQ. 64, 66, 68 and 6A seem to be greater than (GT), less than (LT), greater than or equal (GTE) and less than or equal (LTE) respectively. 74, 76, 78 and 7A appear to be same, only unsigned.
To further complicate matters, the 8x opcodes also implement comparisons (the same six EQU, NEQ, GT, LT, GTE, LTE operations), but for other types than signed and unsigned integers. The second byte after determines the type, with 00 => bool, 01 => string (both from the stack, so both of these sequences consume 2 bytes), and 02, 03 and 04 being various byte comparisons consuming an additional 2 bytes after the type byte. So our simple p-code disassembler which assumes all opcodes but 2C are fixed size needs to be modified to handle these opcodes a little differently.
That's enough for this post. The runtime disassembly is certainly starting to make a bit more sense, but there are plenty of mysteries left to explore.
- 3
0 Comments
Recommended Comments
There are no comments to display.