Sixty four instructions becomes seventy
The six settled back on the twenty fourth, built now. RCAL and RRET are a call that puts nothing back. CALL restores A, B and Data Pointers 0 through 2, which costs ten bytes of Stack and is why a subroutine here can only hand anything back through Q, DP3 or memory. RCAL costs two and restores nothing, which is what a short leaf routine wants and is unsafe in exactly the way the name says. They are a pair because the frames are different sizes: returning from one through the other walks the Stack to somewhere that was never a return address. That was the user's correction to the original proposal, which had a raw call and no raw return. DPUA and DPDA offset a Data Pointer by A; DPUW and DPDW by A and B together, most significant first. DPUP and DPDN take a byte written into the program, so moving a pointer by something just worked out meant storing it and loading it back. Down as well as up on symmetry grounds, which was also the user's call - the argument against it came from counting uses in a corpus written under the constraint. The opcodes sit where they belong: 0x16 and 0x1E immediately below CALL and RET, and 0x4E through 0x51 at the end of the Data Pointer family. All six fit shapes that already existed, so instructiontable.py needed only set membership and both machine side copies of the table regenerated from it unchanged. Checked at every level it exists at: the emulator runs them, the host assembler encodes them, the monitor disassembles all six with the right lengths, and the assembler that runs on the machine builds a program using them byte for byte identically to the host - and that program runs. The recorded test measures what the two calls COST as well as what they put back, because an RCAL that quietly did what CALL does would still return to the right place. It does not survive that: returned through RRET, it hangs. docs.sh can read a two word number now. The count of instructions taking a Data Pointer went past twenty, and the pattern only allowed one word, so the check would have reported that the manual had stopped saying it rather than that the number was wrong. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
00d896e3e7
commit
af0360128b
@@ -37,7 +37,7 @@ AsmShapeSelectors:
|
|||||||
0d0 0d0 0d0 0d1 0d1 0d1 0d2
|
0d0 0d0 0d0 0d1 0d1 0d1 0d2
|
||||||
|
|
||||||
AsmInstructionCount:
|
AsmInstructionCount:
|
||||||
0d64
|
0d70
|
||||||
|
|
||||||
AsmInstructions:
|
AsmInstructions:
|
||||||
0x00 0d0 "ADD "
|
0x00 0d0 "ADD "
|
||||||
@@ -59,9 +59,11 @@ AsmInstructions:
|
|||||||
0x1B 0d1 "BNA "
|
0x1B 0d1 "BNA "
|
||||||
0x1C 0d1 "BNB "
|
0x1C 0d1 "BNB "
|
||||||
0x1D 0d1 "BNC "
|
0x1D 0d1 "BNC "
|
||||||
|
0x16 0d1 "RCAL"
|
||||||
0x17 0d1 "CALL"
|
0x17 0d1 "CALL"
|
||||||
0x18 0d2 "SWI "
|
0x18 0d2 "SWI "
|
||||||
0x19 0d0 "RETI"
|
0x19 0d0 "RETI"
|
||||||
|
0x1E 0d0 "RRET"
|
||||||
0x1F 0d0 "RET "
|
0x1F 0d0 "RET "
|
||||||
0x20 0d0 "RSTA"
|
0x20 0d0 "RSTA"
|
||||||
0x21 0d0 "RSTB"
|
0x21 0d0 "RSTB"
|
||||||
@@ -97,6 +99,10 @@ AsmInstructions:
|
|||||||
0x4B 0d6 "STD "
|
0x4B 0d6 "STD "
|
||||||
0x4C 0d3 "MVSD"
|
0x4C 0d3 "MVSD"
|
||||||
0x4D 0d3 "MVDS"
|
0x4D 0d3 "MVDS"
|
||||||
|
0x4E 0d3 "DPUA"
|
||||||
|
0x4F 0d3 "DPDA"
|
||||||
|
0x50 0d3 "DPUW"
|
||||||
|
0x51 0d3 "DPDW"
|
||||||
0xD0 0d2 "OUTQ"
|
0xD0 0d2 "OUTQ"
|
||||||
0xD1 0d2 "OUTA"
|
0xD1 0d2 "OUTA"
|
||||||
0xD2 0d2 "OUTB"
|
0xD2 0d2 "OUTB"
|
||||||
|
|||||||
@@ -3664,7 +3664,7 @@ ShapeLength:
|
|||||||
0d1 0d3 0d2 0d2 0d3 0d4 0d3
|
0d1 0d3 0d2 0d2 0d3 0d4 0d3
|
||||||
|
|
||||||
InstructionCount:
|
InstructionCount:
|
||||||
0d64
|
0d70
|
||||||
|
|
||||||
; ---- The instruction table ----
|
; ---- The instruction table ----
|
||||||
;
|
;
|
||||||
@@ -3691,9 +3691,11 @@ Instructions:
|
|||||||
0x1B 0d1 "BNA "
|
0x1B 0d1 "BNA "
|
||||||
0x1C 0d1 "BNB "
|
0x1C 0d1 "BNB "
|
||||||
0x1D 0d1 "BNC "
|
0x1D 0d1 "BNC "
|
||||||
|
0x16 0d1 "RCAL"
|
||||||
0x17 0d1 "CALL"
|
0x17 0d1 "CALL"
|
||||||
0x18 0d2 "SWI "
|
0x18 0d2 "SWI "
|
||||||
0x19 0d0 "RETI"
|
0x19 0d0 "RETI"
|
||||||
|
0x1E 0d0 "RRET"
|
||||||
0x1F 0d0 "RET "
|
0x1F 0d0 "RET "
|
||||||
0x20 0d0 "RSTA"
|
0x20 0d0 "RSTA"
|
||||||
0x21 0d0 "RSTB"
|
0x21 0d0 "RSTB"
|
||||||
@@ -3729,6 +3731,10 @@ Instructions:
|
|||||||
0x4B 0d6 "STD "
|
0x4B 0d6 "STD "
|
||||||
0x4C 0d3 "MVSD"
|
0x4C 0d3 "MVSD"
|
||||||
0x4D 0d3 "MVDS"
|
0x4D 0d3 "MVDS"
|
||||||
|
0x4E 0d3 "DPUA"
|
||||||
|
0x4F 0d3 "DPDA"
|
||||||
|
0x50 0d3 "DPUW"
|
||||||
|
0x51 0d3 "DPDW"
|
||||||
0xD0 0d2 "OUTQ"
|
0xD0 0d2 "OUTQ"
|
||||||
0xD1 0d2 "OUTA"
|
0xD1 0d2 "OUTA"
|
||||||
0xD2 0d2 "OUTB"
|
0xD2 0d2 "OUTB"
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
; The six instructions added after the first sixty four, and what each is for.
|
||||||
|
;
|
||||||
|
; Two of them are a call that puts nothing back, and four move a Data Pointer by a value
|
||||||
|
; worked out while the program is running rather than one written into it.
|
||||||
|
;
|
||||||
|
; WHY A SECOND KIND OF CALL. CALL puts A, B and Data Pointers 0 through 2 back the way it
|
||||||
|
; found them, which costs ten bytes of Stack and means a subroutine can only hand anything
|
||||||
|
; back through Q, DP3 or memory. That is the right default and it is what almost everything
|
||||||
|
; here uses. RCAL costs two bytes and puts nothing back at all, which is what a short leaf
|
||||||
|
; routine wants - and it is unsafe in exactly the way its name says, because everything the
|
||||||
|
; callee touches, the caller has lost.
|
||||||
|
;
|
||||||
|
; The two frames are different sizes, so RCAL must be returned from with RRET and CALL with
|
||||||
|
; RET. Crossing them walks the Stack to somewhere that was never a return address.
|
||||||
|
;
|
||||||
|
; WHY OFFSET BY A REGISTER. DPUP and DPDN take a byte written into the program, so moving a
|
||||||
|
; pointer by something just worked out meant storing it and loading it back. DPUA and DPDA
|
||||||
|
; take A; DPUW and DPDW take A and B together, which is how every sixteen bit value on this
|
||||||
|
; machine is carried between registers.
|
||||||
|
;
|
||||||
|
; Written by Anachronaut
|
||||||
|
|
||||||
|
#Program
|
||||||
|
|
||||||
|
start:
|
||||||
|
; ---- Offsetting by a byte, up and then back down ----
|
||||||
|
SETD.0 Text
|
||||||
|
INIA 0d7
|
||||||
|
DPUA.0
|
||||||
|
LDA.0
|
||||||
|
OUTA 0x00 ; the eighth character
|
||||||
|
INIA 0d4
|
||||||
|
DPDA.0
|
||||||
|
LDA.0
|
||||||
|
OUTA 0x00 ; and four before it
|
||||||
|
|
||||||
|
; ---- Offsetting by a whole sixteen bit value ----
|
||||||
|
SETD.1 Text
|
||||||
|
INIA 0x00
|
||||||
|
INIB 0d13
|
||||||
|
DPUW.1
|
||||||
|
LDA.1
|
||||||
|
OUTA 0x00
|
||||||
|
INIA 0x00
|
||||||
|
INIB 0d13
|
||||||
|
DPDW.1
|
||||||
|
LDA.1
|
||||||
|
OUTA 0x00 ; back where it started
|
||||||
|
|
||||||
|
; ---- What each kind of call puts back ----
|
||||||
|
;
|
||||||
|
; The same callee is reached both ways and sets A to something else. After the raw call
|
||||||
|
; that is what A holds; after the safe one it is not.
|
||||||
|
INIA 0x41
|
||||||
|
RCAL wrecker
|
||||||
|
OUTA 0x00 ; Z - the callee's
|
||||||
|
INIA 0x41
|
||||||
|
CALL polite
|
||||||
|
OUTA 0x00 ; A - put back
|
||||||
|
|
||||||
|
; ---- And what each costs ----
|
||||||
|
;
|
||||||
|
; Printed as a digit added to '0', so two bytes reads as "2" and ten reads as the
|
||||||
|
; character ten along from it.
|
||||||
|
MVSD.0
|
||||||
|
PSHD.0
|
||||||
|
POPB
|
||||||
|
POPA
|
||||||
|
SETD.2 Before
|
||||||
|
STB.2
|
||||||
|
|
||||||
|
RCAL rawCost
|
||||||
|
CALL safeCost
|
||||||
|
|
||||||
|
INIA 0x0A
|
||||||
|
OUTA 0x00
|
||||||
|
HALT
|
||||||
|
|
||||||
|
wrecker:
|
||||||
|
INIA 0x5A
|
||||||
|
RRET
|
||||||
|
|
||||||
|
polite:
|
||||||
|
INIA 0x5A
|
||||||
|
RET
|
||||||
|
|
||||||
|
; WRITTEN OUT TWICE RATHER THAN CALLED, because a call would put its own frame down on
|
||||||
|
; top of the one being measured and each of these would report ten bytes more than it
|
||||||
|
; costs. The first version of this did exactly that and printed twelve and twenty.
|
||||||
|
rawCost:
|
||||||
|
MVSD.1
|
||||||
|
PSHD.1
|
||||||
|
POPB
|
||||||
|
POPA
|
||||||
|
SETD.2 Before
|
||||||
|
LDA.2
|
||||||
|
CCF
|
||||||
|
SUB
|
||||||
|
MVQA
|
||||||
|
INIB 0x30
|
||||||
|
CCF
|
||||||
|
ADD
|
||||||
|
MVQA
|
||||||
|
OUTA 0x00
|
||||||
|
RRET
|
||||||
|
|
||||||
|
safeCost:
|
||||||
|
MVSD.1
|
||||||
|
PSHD.1
|
||||||
|
POPB
|
||||||
|
POPA
|
||||||
|
SETD.2 Before
|
||||||
|
LDA.2
|
||||||
|
CCF
|
||||||
|
SUB
|
||||||
|
MVQA
|
||||||
|
INIB 0x30
|
||||||
|
CCF
|
||||||
|
ADD
|
||||||
|
MVQA
|
||||||
|
OUTA 0x00
|
||||||
|
RET
|
||||||
|
|
||||||
|
#Data
|
||||||
|
|
||||||
|
Text:
|
||||||
|
"0123456789abcdefg"
|
||||||
|
Before:
|
||||||
|
0x00
|
||||||
@@ -33,7 +33,7 @@ wrote Asm.sbx: program 7533, data 4099, labels 555
|
|||||||
## The Machine:
|
## The Machine:
|
||||||
|
|
||||||
- **Harvard architecture.** Two 64K memories, one for instructions and one for data. An instruction can only read the second, which is why strings live there and why the memory controller exists.
|
- **Harvard architecture.** Two 64K memories, one for instructions and one for data. An instruction can only read the second, which is why strings live there and why the memory controller exists.
|
||||||
- **Its own instruction set**, 64 instructions, four Data Pointers, and a Q register that holds what the ALU last worked out. Small enough that the table describing it fits in the machine's own memory, which is what lets it disassemble and assemble for itself.
|
- **Its own instruction set**, 70 instructions, four Data Pointers, and a Q register that holds what the ALU last worked out. Small enough that the table describing it fits in the machine's own memory, which is what lets it disassemble and assemble for itself.
|
||||||
- **Interrupts.** Software traps, hardware lines from devices, and faults, all arriving through one vector table with a full context save.
|
- **Interrupts.** Software traps, hardware lines from devices, and faults, all arriving through one vector table with a full context save.
|
||||||
- **A bus programs can enumerate**, so a program can ask what a machine is made of rather than being told.
|
- **A bus programs can enumerate**, so a program can ask what a machine is made of rather than being told.
|
||||||
- **A memory controller** that reads and writes Program Memory, moves blocks between banks, reaches memory that devices bring with them, and guards a range against being written by accident. It is how a SplitBit machine loads a program.
|
- **A memory controller** that reads and writes Program Memory, moves blocks between banks, reaches memory that devices bring with them, and guards a range against being written by accident. It is how a SplitBit machine loads a program.
|
||||||
|
|||||||
@@ -36,9 +36,11 @@ Instruction instruction_set[] = {
|
|||||||
{0x1B, "BNA"},
|
{0x1B, "BNA"},
|
||||||
{0x1C, "BNB"},
|
{0x1C, "BNB"},
|
||||||
{0x1D, "BNC"},
|
{0x1D, "BNC"},
|
||||||
|
{0x16, "RCAL"},
|
||||||
{0x17, "CALL"},
|
{0x17, "CALL"},
|
||||||
{0x18, "SWI"},
|
{0x18, "SWI"},
|
||||||
{0x19, "RETI"},
|
{0x19, "RETI"},
|
||||||
|
{0x1E, "RRET"},
|
||||||
{0x1F, "RET"},
|
{0x1F, "RET"},
|
||||||
// Register Operations:
|
// Register Operations:
|
||||||
{0x20, "RSTA"},
|
{0x20, "RSTA"},
|
||||||
@@ -77,6 +79,10 @@ Instruction instruction_set[] = {
|
|||||||
{0x4B, "STD"},
|
{0x4B, "STD"},
|
||||||
{0x4C, "MVSD"},
|
{0x4C, "MVSD"},
|
||||||
{0x4D, "MVDS"},
|
{0x4D, "MVDS"},
|
||||||
|
{0x4E, "DPUA"},
|
||||||
|
{0x4F, "DPDA"},
|
||||||
|
{0x50, "DPUW"},
|
||||||
|
{0x51, "DPDW"},
|
||||||
// Output Operations:
|
// Output Operations:
|
||||||
{0xD0, "OUTQ"},
|
{0xD0, "OUTQ"},
|
||||||
{0xD1, "OUTA"},
|
{0xD1, "OUTA"},
|
||||||
@@ -123,6 +129,10 @@ int dataPointerOperands(uint8_t opcode) {
|
|||||||
case 0x49: // DPDN
|
case 0x49: // DPDN
|
||||||
case 0x4C: // MVSD
|
case 0x4C: // MVSD
|
||||||
case 0x4D: // MVDS
|
case 0x4D: // MVDS
|
||||||
|
case 0x4E: // DPUA
|
||||||
|
case 0x4F: // DPDA
|
||||||
|
case 0x50: // DPUW
|
||||||
|
case 0x51: // DPDW
|
||||||
return 1;
|
return 1;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -266,6 +266,24 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) {
|
|||||||
cpu->ProgramCounter = destination - 1;
|
cpu->ProgramCounter = destination - 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 0x16:
|
||||||
|
// RCAL - Call, pushing nothing but the return address.
|
||||||
|
//
|
||||||
|
// The unsafe one, and it says so in its name. CALL puts A, B and the first
|
||||||
|
// three Data Pointers back the way it found them, which costs ten bytes of
|
||||||
|
// Stack and means a subroutine can only hand anything back through Q, DP3 or
|
||||||
|
// memory. RCAL costs two bytes and puts nothing back at all: everything the
|
||||||
|
// callee touches, the caller has lost.
|
||||||
|
//
|
||||||
|
// It must be returned from with RRET. The two frames are different sizes, so
|
||||||
|
// returning from one through the other walks the Stack to somewhere that was
|
||||||
|
// never a return address.
|
||||||
|
cpu->Data[cpu->StackPointer] = cpu->ProgramCounter & 0xFF;
|
||||||
|
cpu->StackPointer--;
|
||||||
|
cpu->Data[cpu->StackPointer] = (cpu->ProgramCounter >> 8) & 0xFF;
|
||||||
|
cpu->StackPointer--;
|
||||||
|
genericBranch(cpu);
|
||||||
|
break;
|
||||||
case 0x17:
|
case 0x17:
|
||||||
// CALL - Push the Program Counter to the Stack, and perform an immediate branch.
|
// CALL - Push the Program Counter to the Stack, and perform an immediate branch.
|
||||||
genericCall(cpu);
|
genericCall(cpu);
|
||||||
@@ -344,6 +362,16 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) {
|
|||||||
// same job with its +2, for the same reason.
|
// same job with its +2, for the same reason.
|
||||||
cpu->ProgramCounter = resumeAddress - 1;
|
cpu->ProgramCounter = resumeAddress - 1;
|
||||||
} break;
|
} break;
|
||||||
|
case 0x1E:
|
||||||
|
// RRET - Return from an RCAL, taking back nothing but the return address.
|
||||||
|
cpu->StackPointer++;
|
||||||
|
cpu->ProgramCounter = (uint16_t)cpu->Data[cpu->StackPointer] << 8;
|
||||||
|
cpu->StackPointer++;
|
||||||
|
cpu->ProgramCounter = cpu->ProgramCounter | (uint16_t)cpu->Data[cpu->StackPointer];
|
||||||
|
// Two on, to step over the address the RCAL branched through, exactly as RET
|
||||||
|
// does. Everything else RET restores, this deliberately does not.
|
||||||
|
cpu->ProgramCounter += 2;
|
||||||
|
break;
|
||||||
case 0x1F:
|
case 0x1F:
|
||||||
// RET - Return from subroutine, restore the registers and set the Program Counter to the Return Address.
|
// RET - Return from subroutine, restore the registers and set the Program Counter to the Return Address.
|
||||||
// Pop A from the Stack.
|
// Pop A from the Stack.
|
||||||
@@ -558,6 +586,36 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) {
|
|||||||
*target -= cpu->Program[cpu->ProgramCounter];
|
*target -= cpu->Program[cpu->ProgramCounter];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 0x4E: {
|
||||||
|
// DPUA - Offset the selected Data Pointer up by A.
|
||||||
|
//
|
||||||
|
// A rather than Q, because Q is what the ALU last worked out and would be
|
||||||
|
// gone by the time anything had been added to it. Working a step out and then
|
||||||
|
// moving a pointer by it took a store and a reload before this existed.
|
||||||
|
uint16_t *target = selectDataPointer(cpu);
|
||||||
|
*target += cpu->A;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x4F: {
|
||||||
|
// DPDA - Offset the selected Data Pointer down by A.
|
||||||
|
uint16_t *target = selectDataPointer(cpu);
|
||||||
|
*target -= cpu->A;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x50: {
|
||||||
|
// DPUW - Offset the selected Data Pointer up by A and B together, A being the
|
||||||
|
// most significant, which is how every sixteen bit value on this machine is
|
||||||
|
// carried between a pair of registers.
|
||||||
|
uint16_t *target = selectDataPointer(cpu);
|
||||||
|
*target += ((uint16_t)cpu->A << 8) | (uint16_t)cpu->B;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x51: {
|
||||||
|
// DPDW - Offset the selected Data Pointer down by A and B together.
|
||||||
|
uint16_t *target = selectDataPointer(cpu);
|
||||||
|
*target -= ((uint16_t)cpu->A << 8) | (uint16_t)cpu->B;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 0x4A: {
|
case 0x4A: {
|
||||||
// LDD - Load the first Data Pointer from the two bytes of Data Memory
|
// LDD - Load the first Data Pointer from the two bytes of Data Memory
|
||||||
// addressed by the second. Byte order matches everywhere else an address
|
// addressed by the second. Byte order matches everywhere else an address
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ A conditional branch reads the thing it names at the moment it runs. BRA looks a
|
|||||||
|
|
||||||
## Naming a Data Pointer:
|
## Naming a Data Pointer:
|
||||||
|
|
||||||
Seventeen instructions work through a Data Pointer. Each of them carries a selector byte immediately after its opcode, naming which Data Pointer it means. LDD and STD move a pointer through a pointer, so they carry two selectors, the first naming the pointer being moved and the second naming the pointer that addresses it.
|
Twenty one instructions work through a Data Pointer. Each of them carries a selector byte immediately after its opcode, naming which Data Pointer it means. LDD and STD move a pointer through a pointer, so they carry two selectors, the first naming the pointer being moved and the second naming the pointer that addresses it.
|
||||||
|
|
||||||
The selector is a full byte, but only enough of it is read to choose among the Data Pointers the machine has. A selector larger than the highest numbered pointer wraps around rather than being rejected, so it is the assembler's job to refuse to write one.
|
The selector is a full byte, but only enough of it is read to choose among the Data Pointers the machine has. A selector larger than the highest numbered pointer wraps around rather than being rejected, so it is the assembler's job to refuse to write one.
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ The Bytes column is the total length of the instruction, counting its opcode, an
|
|||||||
| 07 | SHL | 1 | A and B form a circular shift register. Rotate this register left. |
|
| 07 | SHL | 1 | A and B form a circular shift register. Rotate this register left. |
|
||||||
| 08 | SHR | 1 | A and B form a circular shift register. Rotate this register right. |
|
| 08 | SHR | 1 | A and B form a circular shift register. Rotate this register right. |
|
||||||
|
|
||||||
### Branch and Subroutine Operations: 14 Instructions
|
### Branch and Subroutine Operations: 16 Instructions
|
||||||
| Hex Code | Mnemonic | Bytes | Description |
|
| Hex Code | Mnemonic | Bytes | Description |
|
||||||
| -- | ---- | -- | -- |
|
| -- | ---- | -- | -- |
|
||||||
| 10 | BRI | 3 | Branch Immediately. Loads the immediate next two bytes of Program Memory into the Program Counter, first the most significant byte, then the least. |
|
| 10 | BRI | 3 | Branch Immediately. Loads the immediate next two bytes of Program Memory into the Program Counter, first the most significant byte, then the least. |
|
||||||
@@ -89,9 +89,11 @@ The Bytes column is the total length of the instruction, counting its opcode, an
|
|||||||
| 1B | BNA | 3 | Branch if A is not zero. |
|
| 1B | BNA | 3 | Branch if A is not zero. |
|
||||||
| 1C | BNB | 3 | Branch if B is not zero. |
|
| 1C | BNB | 3 | Branch if B is not zero. |
|
||||||
| 1D | BNC | 3 | Branch if the Carry Flag is clear. |
|
| 1D | BNC | 3 | Branch if the Carry Flag is clear. |
|
||||||
|
| 16 | RCAL | 3 | Raw call. Pushes only the Program Counter, then performs an immediate branch. Two bytes of Stack, and nothing is put back. Must be returned from with RRET. |
|
||||||
| 17 | CALL | 3 | Call subroutine. Pushes the Program Counter, Data Pointers 0 through 2, B and A to the Stack, then performs an immediate branch. This costs ten bytes of Stack. |
|
| 17 | CALL | 3 | Call subroutine. Pushes the Program Counter, Data Pointers 0 through 2, B and A to the Stack, then performs an immediate branch. This costs ten bytes of Stack. |
|
||||||
| 18 | SWI | 2 | Software Interrupt. The next byte names a software vector. Pushes an interrupt frame and dispatches through it. Never masked. |
|
| 18 | SWI | 2 | Software Interrupt. The next byte names a software vector. Pushes an interrupt frame and dispatches through it. Never masked. |
|
||||||
| 19 | RETI | 1 | Return from an interrupt. Restores everything the frame holds and carries on from where the interrupt arrived. |
|
| 19 | RETI | 1 | Return from an interrupt. Restores everything the frame holds and carries on from where the interrupt arrived. |
|
||||||
|
| 1E | RRET | 1 | Return from a raw call. Takes back the Program Counter and nothing else. |
|
||||||
| 1F | RET | 1 | Return from subroutine. Restores A, B, and Data Pointers 0 through 2 from the Stack, then sets the Program Counter to the instruction after the CALL. Data Pointer 3 and Q are left as the subroutine leaves them. |
|
| 1F | RET | 1 | Return from subroutine. Restores A, B, and Data Pointers 0 through 2 from the Stack, then sets the Program Counter to the instruction after the CALL. Data Pointer 3 and Q are left as the subroutine leaves them. |
|
||||||
|
|
||||||
### Register Operations: 13 Instructions
|
### Register Operations: 13 Instructions
|
||||||
@@ -124,7 +126,7 @@ Q is where every ALU result lands, and Q is not itself an ALU operand, so MVQA a
|
|||||||
| 35 | POPB | 1 | Reads the location referenced by the Stack Pointer from Data Memory into B then increments the Stack Pointer. |
|
| 35 | POPB | 1 | Reads the location referenced by the Stack Pointer from Data Memory into B then increments the Stack Pointer. |
|
||||||
| 36 | POPD | 2 | Restores the named Data Pointer from the stack, increments the Stack Pointer by two. |
|
| 36 | POPD | 2 | Restores the named Data Pointer from the stack, increments the Stack Pointer by two. |
|
||||||
|
|
||||||
### Data Operations: 14 Instructions
|
### Data Operations: 18 Instructions
|
||||||
| Hex Code | Mnemonic | Bytes | Description |
|
| Hex Code | Mnemonic | Bytes | Description |
|
||||||
| -- | ---- | -- | -- |
|
| -- | ---- | -- | -- |
|
||||||
| 40 | INCD | 2 | Increments the named Data Pointer. |
|
| 40 | INCD | 2 | Increments the named Data Pointer. |
|
||||||
@@ -141,6 +143,10 @@ Q is where every ALU result lands, and Q is not itself an ALU operand, so MVQA a
|
|||||||
| 4B | STD | 3 | Stores the first named Data Pointer into the two bytes of Data Memory addressed by the second, most significant byte first. |
|
| 4B | STD | 3 | Stores the first named Data Pointer into the two bytes of Data Memory addressed by the second, most significant byte first. |
|
||||||
| 4C | MVSD | 2 | Copies the Stack Pointer into the named Data Pointer. The Stack Pointer itself is unchanged. |
|
| 4C | MVSD | 2 | Copies the Stack Pointer into the named Data Pointer. The Stack Pointer itself is unchanged. |
|
||||||
| 4D | MVDS | 2 | Copies the named Data Pointer into the Stack Pointer, moving the Stack. Read The Stack Pointer, Set By Hand before using it. |
|
| 4D | MVDS | 2 | Copies the named Data Pointer into the Stack Pointer, moving the Stack. Read The Stack Pointer, Set By Hand before using it. |
|
||||||
|
| 4E | DPUA | 2 | Offsets the named Data Pointer up by A. |
|
||||||
|
| 4F | DPDA | 2 | Offsets the named Data Pointer down by A. |
|
||||||
|
| 50 | DPUW | 2 | Offsets the named Data Pointer up by A and B together, A being the most significant. |
|
||||||
|
| 51 | DPDW | 2 | Offsets the named Data Pointer down by A and B together, A being the most significant. |
|
||||||
|
|
||||||
BRD is the only branch whose destination is not written into the program. Every other branch carries the address it goes to, fixed when the program was assembled; BRD takes it from a Data Pointer, which is what makes a table of addresses something a program can dispatch through rather than only read. Together with LDD it turns the Data Segment into somewhere a program can keep a list of places to go.
|
BRD is the only branch whose destination is not written into the program. Every other branch carries the address it goes to, fixed when the program was assembled; BRD takes it from a Data Pointer, which is what makes a table of addresses something a program can dispatch through rather than only read. Together with LDD it turns the Data Segment into somewhere a program can keep a list of places to go.
|
||||||
|
|
||||||
|
|||||||
+9
-2
@@ -130,11 +130,18 @@ for m in re.finditer(r'^### (.+?) Operations: (\d+) Instructions?$', pm, re.M):
|
|||||||
# goes stale quietly: adding an instruction that takes a selector leaves the sentence
|
# goes stale quietly: adding an instruction that takes a selector leaves the sentence
|
||||||
# looking perfectly reasonable and wrong. dataPointerOperands is the list, so it is the
|
# looking perfectly reasonable and wrong. dataPointerOperands is the list, so it is the
|
||||||
# one to believe.
|
# one to believe.
|
||||||
|
# Past twenty the number is two words, the way this manual writes every other one, so the
|
||||||
|
# pattern has to allow a second - and the count going past twenty is exactly the sort of
|
||||||
|
# thing that would otherwise turn "the manual is wrong" into "the manual has stopped
|
||||||
|
# saying it", which reads as a different kind of problem.
|
||||||
words = {12: "Twelve", 13: "Thirteen", 14: "Fourteen", 15: "Fifteen", 16: "Sixteen",
|
words = {12: "Twelve", 13: "Thirteen", 14: "Fourteen", 15: "Fifteen", 16: "Sixteen",
|
||||||
17: "Seventeen", 18: "Eighteen", 19: "Nineteen", 20: "Twenty"}
|
17: "Seventeen", 18: "Eighteen", 19: "Nineteen", 20: "Twenty",
|
||||||
|
21: "Twenty one", 22: "Twenty two", 23: "Twenty three", 24: "Twenty four",
|
||||||
|
25: "Twenty five", 26: "Twenty six"}
|
||||||
selectors = asmc[asmc.index("int dataPointerOperands"):asmc.index("uint8_t getOpcode")]
|
selectors = asmc[asmc.index("int dataPointerOperands"):asmc.index("uint8_t getOpcode")]
|
||||||
taking = len(re.findall(r'^\s*case 0x[0-9A-Fa-f]{2}:', selectors, re.M))
|
taking = len(re.findall(r'^\s*case 0x[0-9A-Fa-f]{2}:', selectors, re.M))
|
||||||
said = re.search(r'^([A-Z][a-z]+) instructions work through a Data Pointer\.', pm, re.M)
|
said = re.search(r'^([A-Z][a-z]+(?: [a-z]+)?) instructions work through a Data Pointer\.',
|
||||||
|
pm, re.M)
|
||||||
if not said:
|
if not said:
|
||||||
problems.append("the manual no longer says how many instructions take a Data Pointer")
|
problems.append("the manual no longer says how many instructions take a Data Pointer")
|
||||||
elif said.group(1) != words.get(taking):
|
elif said.group(1) != words.get(taking):
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
73d0ZA2:
|
||||||
|
Execution halted.
|
||||||
|
[exit 0]
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""The instruction table, as the assembler has it.
|
"""The instruction table, as the assembler has it.
|
||||||
|
|
||||||
The monitor needs the same 64 instructions the assembler does, with the same names and the
|
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
|
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
|
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
|
everything after it wrong too. So the table is generated from assembly.c rather than typed
|
||||||
@@ -14,11 +14,12 @@ Shapes are what follows the opcode:
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
ADDRESS = {0x10, 0x11, 0x12, 0x13, 0x14, 0x17, 0x1A, 0x1B, 0x1C, 0x1D}
|
ADDRESS = {0x10, 0x11, 0x12, 0x13, 0x14, 0x16, 0x17, 0x1A, 0x1B, 0x1C, 0x1D}
|
||||||
ONE_BYTE = {0x18, 0x26, 0x27}
|
ONE_BYTE = {0x18, 0x26, 0x27}
|
||||||
TWO_SELECTORS = {0x4A, 0x4B}
|
TWO_SELECTORS = {0x4A, 0x4B}
|
||||||
SELECTOR = {0x15, 0x33, 0x36, 0x40, 0x41, 0x42, 0x43, 0x44,
|
SELECTOR = {0x15, 0x33, 0x36, 0x40, 0x41, 0x42, 0x43, 0x44,
|
||||||
0x45, 0x46, 0x47, 0x48, 0x49, 0x4C, 0x4D}
|
0x45, 0x46, 0x47, 0x48, 0x49, 0x4C, 0x4D,
|
||||||
|
0x4E, 0x4F, 0x50, 0x51}
|
||||||
|
|
||||||
|
|
||||||
def shapeOf(opcode):
|
def shapeOf(opcode):
|
||||||
|
|||||||
@@ -493,6 +493,16 @@ lib-print | Libraries/print.asm | xfail | -
|
|||||||
# changed in the middle of the Data Segment and every label after it came out nine bytes
|
# changed in the middle of the Data Segment and every label after it came out nine bytes
|
||||||
# wrong, in a file that still had a valid header and a plausible length.
|
# wrong, in a file that still had a valid header and a plausible length.
|
||||||
stringKeyword | testPrograms/stringKeyword.asm | run | - | -
|
stringKeyword | testPrograms/stringKeyword.asm | run | - | -
|
||||||
|
# The six instructions added after the first sixty four. Four of them move a Data Pointer
|
||||||
|
# by a register rather than by a byte written into the program, and two are a call that
|
||||||
|
# puts nothing back.
|
||||||
|
#
|
||||||
|
# What is recorded is the DIFFERENCE between the two kinds of call, twice over: the same
|
||||||
|
# callee sets A, and after the raw one that is what A holds while after the safe one it is
|
||||||
|
# not; and each reports how far the Stack came down, which is two bytes against ten. A
|
||||||
|
# version of RCAL that quietly did what CALL does would pass a test that only checked it
|
||||||
|
# returned to the right place.
|
||||||
|
rawCallAndOffsets | testPrograms/rawCallAndOffsets.asm | run | - | -
|
||||||
printDecimalTest | testPrograms/printDecimalTest.asm | xfail | - | -
|
printDecimalTest | testPrograms/printDecimalTest.asm | xfail | - | -
|
||||||
printDigitTest | testPrograms/printDigitTest.asm | xfail | - | -
|
printDigitTest | testPrograms/printDigitTest.asm | xfail | - | -
|
||||||
printHexTest | testPrograms/printHexTest.asm | xfail | - | -
|
printHexTest | testPrograms/printHexTest.asm | xfail | - | -
|
||||||
|
|||||||
Reference in New Issue
Block a user