diff --git a/Programs/CosmOS/Assembler/Asm.asm b/Programs/CosmOS/Assembler/Asm.asm index 4c63542..39308cb 100644 --- a/Programs/CosmOS/Assembler/Asm.asm +++ b/Programs/CosmOS/Assembler/Asm.asm @@ -203,7 +203,7 @@ passNotVectorName: BNQ passNotSwi SETD.0 ClsOpcode LDA.0 - INIB 0x18 + INIB 0x72 ; SWI. Moved with the rest of the subroutine block. XOR BNQ passNotSwi INIA 0d1 diff --git a/Programs/CosmOS/Assembler/table.asm b/Programs/CosmOS/Assembler/table.asm index 0e55995..f0b2f71 100644 --- a/Programs/CosmOS/Assembler/table.asm +++ b/Programs/CosmOS/Assembler/table.asm @@ -40,31 +40,31 @@ AsmInstructionCount: 0d71 AsmInstructions: - 0x00 0d0 "ADD " - 0x01 0d0 "SUB " - 0x02 0d0 "AND " - 0x03 0d0 "OR " - 0x04 0d0 "XOR " - 0x05 0d0 "NOTA" - 0x06 0d0 "NOTB" - 0x07 0d0 "SHL " - 0x08 0d0 "SHR " - 0x10 0d1 "BRI " - 0x11 0d1 "BRQ " - 0x12 0d1 "BRA " - 0x13 0d1 "BRB " - 0x14 0d1 "BRC " - 0x15 0d3 "BRD " - 0x1A 0d1 "BNQ " - 0x1B 0d1 "BNA " - 0x1C 0d1 "BNB " - 0x1D 0d1 "BNC " - 0x16 0d1 "RCAL" - 0x17 0d1 "CALL" - 0x18 0d2 "SWI " - 0x19 0d0 "RETI" - 0x1E 0d0 "RRET" - 0x1F 0d0 "RET " + 0x10 0d0 "ADD " + 0x11 0d0 "SUB " + 0x12 0d0 "AND " + 0x13 0d0 "OR " + 0x14 0d0 "XOR " + 0x15 0d0 "NOTA" + 0x16 0d0 "NOTB" + 0x17 0d0 "SHL " + 0x18 0d0 "SHR " + 0x60 0d1 "BRI " + 0x61 0d1 "BRQ " + 0x62 0d1 "BRA " + 0x63 0d1 "BRB " + 0x64 0d1 "BRC " + 0x65 0d3 "BRD " + 0x66 0d1 "BNQ " + 0x67 0d1 "BNA " + 0x68 0d1 "BNB " + 0x69 0d1 "BNC " + 0x70 0d1 "RCAL" + 0x71 0d1 "CALL" + 0x72 0d2 "SWI " + 0x73 0d0 "RETI" + 0x74 0d0 "RRET" + 0x75 0d0 "RET " 0x20 0d0 "RSTA" 0x21 0d0 "RSTB" 0x22 0d0 "INCA" diff --git a/Programs/CosmOS/Source/cosmos.asm b/Programs/CosmOS/Source/cosmos.asm index 4cd1e12..ee510c1 100644 --- a/Programs/CosmOS/Source/cosmos.asm +++ b/Programs/CosmOS/Source/cosmos.asm @@ -3837,31 +3837,31 @@ InstructionCount: ; it by Tests/docs.sh. Seven bytes each: the opcode, the shape, and four characters of name ; with the zero the assembler puts after a string. Instructions: - 0x00 0d0 "ADD " - 0x01 0d0 "SUB " - 0x02 0d0 "AND " - 0x03 0d0 "OR " - 0x04 0d0 "XOR " - 0x05 0d0 "NOTA" - 0x06 0d0 "NOTB" - 0x07 0d0 "SHL " - 0x08 0d0 "SHR " - 0x10 0d1 "BRI " - 0x11 0d1 "BRQ " - 0x12 0d1 "BRA " - 0x13 0d1 "BRB " - 0x14 0d1 "BRC " - 0x15 0d3 "BRD " - 0x1A 0d1 "BNQ " - 0x1B 0d1 "BNA " - 0x1C 0d1 "BNB " - 0x1D 0d1 "BNC " - 0x16 0d1 "RCAL" - 0x17 0d1 "CALL" - 0x18 0d2 "SWI " - 0x19 0d0 "RETI" - 0x1E 0d0 "RRET" - 0x1F 0d0 "RET " + 0x10 0d0 "ADD " + 0x11 0d0 "SUB " + 0x12 0d0 "AND " + 0x13 0d0 "OR " + 0x14 0d0 "XOR " + 0x15 0d0 "NOTA" + 0x16 0d0 "NOTB" + 0x17 0d0 "SHL " + 0x18 0d0 "SHR " + 0x60 0d1 "BRI " + 0x61 0d1 "BRQ " + 0x62 0d1 "BRA " + 0x63 0d1 "BRB " + 0x64 0d1 "BRC " + 0x65 0d3 "BRD " + 0x66 0d1 "BNQ " + 0x67 0d1 "BNA " + 0x68 0d1 "BNB " + 0x69 0d1 "BNC " + 0x70 0d1 "RCAL" + 0x71 0d1 "CALL" + 0x72 0d2 "SWI " + 0x73 0d0 "RETI" + 0x74 0d0 "RRET" + 0x75 0d0 "RET " 0x20 0d0 "RSTA" 0x21 0d0 "RSTB" 0x22 0d0 "INCA" diff --git a/Programs/testPrograms/loaderTest.asm b/Programs/testPrograms/loaderTest.asm index bd40d7c..af31b1d 100644 --- a/Programs/testPrograms/loaderTest.asm +++ b/Programs/testPrograms/loaderTest.asm @@ -114,6 +114,12 @@ Back: ; by hand because SplitBit has no linker: to this program it is just so much data. ; ; INIA 'l' OUTA 0 INIA 'o' OUTA 0 ... and so on, then RETI +; +; THESE ARE OPCODES AND THEY MOVE WHEN THE MAP MOVES. Nothing checks them - to the +; assembler they are numbers, and to this program they are data - so an opcode +; reorganisation has to come here by hand. It has happened once: RETI was 0x19 before the +; branch and subroutine blocks were split apart, and this was the only place in the corpus +; that noticed, by faulting on a byte that had stopped being an instruction. Payload: 0x26 0x6C 0xD1 0x00 0x26 0x6F 0xD1 0x00 @@ -122,4 +128,4 @@ Payload: 0x26 0x65 0xD1 0x00 0x26 0x64 0xD1 0x00 0x26 0x0A 0xD1 0x00 - 0x19 + 0x73 ; RETI diff --git a/Programs/testPrograms/runOffTest.asm b/Programs/testPrograms/runOffTest.asm new file mode 100644 index 0000000..0fb4f3f --- /dev/null +++ b/Programs/testPrograms/runOffTest.asm @@ -0,0 +1,21 @@ +; runOffTest.asm +; A program that runs off the end of itself. +; +; THIS IS WHY 0x00 IS NOT AN INSTRUCTION. Program Memory that has never been written, or a +; load that stopped part way and left zeroes in its tail, used to read as a long run of +; additions - the machine would carry on through them, arrive somewhere unpredictable, and +; whatever went wrong there would be a long way from the byte that caused it. +; +; Nothing in 0x00 to 0x0F is an instruction now, so a run into blank memory faults where it +; is met and says the address. That is the difference between a diagnosis and a search, and +; it costs an opcode block nobody was using. +; +; The program does not halt. It is not supposed to: falling off the end IS the test. +; +; Written by Anachronaut + +#Program + +start: + INIA 0x2E ; "." + OUTA 0x00 diff --git a/Source/Assembler/assembly.c b/Source/Assembler/assembly.c index ba0b7a5..25bf8b6 100644 --- a/Source/Assembler/assembly.c +++ b/Source/Assembler/assembly.c @@ -12,36 +12,50 @@ typedef struct { } Instruction; Instruction instruction_set[] = { + // ---- Nothing at all in 0x00 to 0x0F ---- + // + // Kept empty on purpose. Program Memory that has never been written, or a load that + // stopped part way and left zeroes in its tail, used to read as a long run of + // additions and then do something unpredictable a long way from the cause. An + // unassigned byte faults where it is met, with the address, which is the difference + // between a diagnosis and a search. + // // Arithmetic and Logic Operations: - {0x00, "ADD"}, - {0x01, "SUB"}, - {0x02, "AND"}, - {0x03, "OR"}, - {0x04, "XOR"}, - {0x05, "NOTA"}, - {0x06, "NOTB"}, - {0x07, "SHL"}, - {0x08, "SHR"}, + {0x10, "ADD"}, + {0x11, "SUB"}, + {0x12, "AND"}, + {0x13, "OR"}, + {0x14, "XOR"}, + {0x15, "NOTA"}, + {0x16, "NOTB"}, + {0x17, "SHL"}, + {0x18, "SHR"}, // Branch Operations: - {0x10, "BRI"}, - {0x11, "BRQ"}, - {0x12, "BRA"}, - {0x13, "BRB"}, - {0x14, "BRC"}, - {0x15, "BRD"}, + {0x60, "BRI"}, + {0x61, "BRQ"}, + {0x62, "BRA"}, + {0x63, "BRB"}, + {0x64, "BRC"}, + {0x65, "BRD"}, // The same four conditions the other way round. A quarter of the conditional // branches in the corpus were a branch over an unconditional one before these // existed, each of them needing a label invented only to be jumped past. - {0x1A, "BNQ"}, - {0x1B, "BNA"}, - {0x1C, "BNB"}, - {0x1D, "BNC"}, - {0x16, "RCAL"}, - {0x17, "CALL"}, - {0x18, "SWI"}, - {0x19, "RETI"}, - {0x1E, "RRET"}, - {0x1F, "RET"}, + {0x66, "BNQ"}, + {0x67, "BNA"}, + {0x68, "BNB"}, + {0x69, "BNC"}, + // Subroutine Operations: + // + // A block of their own since the branches and these outgrew one nibble between them. + // Each raw form sits immediately below the ordinary one it cannot be mixed with: RCAL + // under CALL, RRET under RET, because the frames differ and returning through the + // wrong one takes the machine somewhere nobody named. + {0x70, "RCAL"}, + {0x71, "CALL"}, + {0x72, "SWI"}, + {0x73, "RETI"}, + {0x74, "RRET"}, + {0x75, "RET"}, // Register Operations: {0x20, "RSTA"}, {0x21, "RSTB"}, @@ -115,7 +129,7 @@ int dataPointerOperands(uint8_t opcode) { case 0x4A: // LDD case 0x4B: // STD return 2; - case 0x15: // BRD + case 0x65: // BRD case 0x33: // PSHD case 0x36: // POPD case 0x40: // INCD diff --git a/Source/Assembler/firstPass.c b/Source/Assembler/firstPass.c index 8d8de2e..d644f69 100644 --- a/Source/Assembler/firstPass.c +++ b/Source/Assembler/firstPass.c @@ -385,7 +385,7 @@ int loadFile(intermediateElement **intermediateArray, char *fileName, int *inter && (*intermediateArray)[*intermediateIndex].type == LABEL && *intermediateIndex > 0 && (*intermediateArray)[*intermediateIndex - 1].type == INSTRUCTION - && (*intermediateArray)[*intermediateIndex - 1].byteValue == 0x18) { + && (*intermediateArray)[*intermediateIndex - 1].byteValue == 0x72) { (*intermediateArray)[*intermediateIndex].type = VECTOR_REFERENCE; (*intermediateArray)[*intermediateIndex].byteLength = 1; } diff --git a/Source/Assembler/secondPass.c b/Source/Assembler/secondPass.c index 5e2147b..fcd68da 100644 --- a/Source/Assembler/secondPass.c +++ b/Source/Assembler/secondPass.c @@ -512,17 +512,17 @@ static void checkOperands(intermediateElement *intermediateArray, int arraySize, const char *problem = NULL; // Listed rather than matched on the high nibble, because not every instruction in - // the branch block takes an address: RET has none, and BRD gets its destination - // from a Data Pointer instead of from the program. - if (opcode == 0x10 || opcode == 0x11 || opcode == 0x12 || - opcode == 0x13 || opcode == 0x14 || opcode == 0x17 || - opcode == 0x1A || opcode == 0x1B || opcode == 0x1C || opcode == 0x1D) { + // these two blocks takes an address: RET has none, and BRD gets its destination from + // a Data Pointer instead of from the program. + if (opcode == 0x60 || opcode == 0x61 || opcode == 0x62 || + opcode == 0x63 || opcode == 0x64 || opcode == 0x71 || + opcode == 0x66 || opcode == 0x67 || opcode == 0x68 || opcode == 0x69) { // Branches and CALL take a two byte address, which only a label can supply. if (nextType != LABEL) problem = "Branch without label."; } else if ((opcode & 0xF0) == 0xD0 || (opcode & 0xF0) == 0xE0) { // The instruction is either an input or output and must be followed by a value. if (nextType != VALUE) problem = "I/O without destination port."; - } else if (opcode == 0x18) { + } else if (opcode == 0x72) { // SWI names a vector, either by the name it was given in the Vector Segment or, // rarely, as a literal number. Without one it swallows whatever follows it and // every address after that shifts. diff --git a/Source/Emulator/cpu.c b/Source/Emulator/cpu.c index e0188b1..806b296 100644 --- a/Source/Emulator/cpu.c +++ b/Source/Emulator/cpu.c @@ -203,7 +203,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { uint16_t result; switch(Instruction) { // 0x - Arithmetic and Logic Operations. - case 0x00: + case 0x10: // ADD - A + B + Carry -> Q result = (uint16_t)cpu->A + (uint16_t)cpu->B + (cpu->Status & STATUS_CARRY); if (result > 255) { @@ -213,7 +213,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { } cpu->Q = result & 0xFF; break; - case 0x01: + case 0x11: // SUB - A - B - Carry -> Q result = (uint16_t)cpu->A - (uint16_t)cpu->B - (cpu->Status & STATUS_CARRY); if (result > 255) { @@ -223,34 +223,34 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { } cpu->Q = result & 0xFF; break; - case 0x02: + case 0x12: // AND - A and B -> Q cpu->Q = cpu->A&cpu->B; break; - case 0x03: + case 0x13: // OR - A or B -> Q cpu->Q = cpu->A|cpu->B; break; - case 0x04: + case 0x14: // XOR - A xor B -> Q cpu->Q = cpu->A^cpu->B; break; - case 0x05: + case 0x15: // NOTA - not A -> Q cpu->Q = ~cpu->A; break; - case 0x06: + case 0x16: // NOTB - not B -> Q cpu->Q = ~cpu->B; break; - case 0x07: + case 0x17: // SHL - Shift AB left. shiftRegister = ((uint16_t)cpu->A << 8) | cpu->B; shiftRegister = (shiftRegister << 1) | (shiftRegister >> 15); cpu->A = shiftRegister >> 8; cpu->B = shiftRegister & 0xFF; break; - case 0x08: + case 0x18: // SHR - Shift AB right. shiftRegister = ((uint16_t)cpu->A << 8) | cpu->B; shiftRegister = (shiftRegister >> 1) | (shiftRegister << 15); @@ -260,11 +260,11 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { // // 1x - Branch Operations: // - case 0x10: + case 0x60: // BRI - Branch Immediately genericBranch(cpu); break; - case 0x11: + case 0x61: // BRQ - Branch if Q = 0 if(cpu->Q == 0) { genericBranch(cpu); @@ -273,7 +273,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { cpu->ProgramCounter+=2; } break; - case 0x12: + case 0x62: // BRA - Branch if A = 0 if(cpu->A == 0) { genericBranch(cpu); @@ -282,7 +282,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { cpu->ProgramCounter+=2; } break; - case 0x13: + case 0x63: // BRB - if B = 0 if(cpu->B == 0) { @@ -291,7 +291,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { cpu->ProgramCounter+=2; } break; - case 0x14: + case 0x64: // BRC - Do an immediate branch if the Carry Flag is set. if (cpu->Status & STATUS_CARRY) { genericBranch(cpu); @@ -299,7 +299,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { cpu->ProgramCounter+=2; } break; - case 0x15: { + case 0x65: { // BRD - Branch to the address held in a Data Pointer. // This is the only branch whose destination is not written into the // program, which is what makes a table of addresses something a program @@ -309,7 +309,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { cpu->ProgramCounter = destination - 1; } break; - case 0x16: + case 0x70: // 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 @@ -327,11 +327,11 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { cpu->StackPointer--; genericBranch(cpu); break; - case 0x17: + case 0x71: // CALL - Push the Program Counter to the Stack, and perform an immediate branch. genericCall(cpu); break; - case 0x1A: + case 0x66: // BNQ - Branch if Q is not 0. if(cpu->Q != 0) { genericBranch(cpu); @@ -339,7 +339,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { cpu->ProgramCounter+=2; } break; - case 0x1B: + case 0x67: // BNA - Branch if A is not 0. if(cpu->A != 0) { genericBranch(cpu); @@ -347,7 +347,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { cpu->ProgramCounter+=2; } break; - case 0x1C: + case 0x68: // BNB - Branch if B is not 0. if(cpu->B != 0) { genericBranch(cpu); @@ -355,7 +355,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { cpu->ProgramCounter+=2; } break; - case 0x1D: + case 0x69: // BNC - Branch if the Carry Flag is clear. if (!(cpu->Status & STATUS_CARRY)) { genericBranch(cpu); @@ -363,7 +363,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { cpu->ProgramCounter+=2; } break; - case 0x18: { + case 0x72: { // SWI - Software Interrupt. The byte after the opcode names the vector. // Never masked: this is an instruction the program deliberately ran, not // something a device asked for. @@ -378,7 +378,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { cpu->ProgramCounter = site - 1; } } break; - case 0x19: { + case 0x73: { // RETI - Return from an interrupt. Pops the frame in the exact reverse of // the order enterInterrupt pushed it. cpu->StackPointer++; @@ -405,7 +405,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { // same job with its +2, for the same reason. cpu->ProgramCounter = resumeAddress - 1; } break; - case 0x1E: + case 0x74: // RRET - Return from an RCAL, taking back nothing but the return address. cpu->StackPointer++; cpu->ProgramCounter = (uint16_t)readData(cpu, cpu->StackPointer) << 8; @@ -415,7 +415,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { // does. Everything else RET restores, this deliberately does not. cpu->ProgramCounter += 2; break; - case 0x1F: + case 0x75: // RET - Return from subroutine, restore the registers and set the Program Counter to the Return Address. // Pop A from the Stack. cpu->StackPointer++; diff --git a/SplitBit Assembler Manual.md b/SplitBit Assembler Manual.md index a5641d9..328cf69 100644 --- a/SplitBit Assembler Manual.md +++ b/SplitBit Assembler Manual.md @@ -233,7 +233,7 @@ A third segment may follow them, marked `VEC`, holding the vectors a program nam Here is the hello world program from the Programming Manual, assembled and dumped as hex: ``` -53 50 42 54 01 00 00 00 00 50 52 47 00 11 42 00 12 00 0c d1 00 40 00 10 00 00 26 0a d1 00 ff 44 +53 50 42 54 01 00 00 00 00 50 52 47 00 11 42 00 62 00 0c d1 00 40 00 60 00 00 26 0a d1 00 ff 44 41 54 00 0e 48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21 00 ``` @@ -246,7 +246,7 @@ Taken apart: | `00 00 00 00` | No features required | | `50 52 47` | `PRG` | | `00 11` | The Program Segment is 17 bytes long | -| `42 00 12 00 0c d1 00 40 00 10 00 00 26 0a d1 00 ff` | The Program Segment | +| `42 00 62 00 0c d1 00 40 00 60 00 00 26 0a d1 00 ff` | The Program Segment | | `44 41 54` | `DAT` | | `00 0e` | The Data Segment is 14 bytes long | | `48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21 00` | The Data Segment | diff --git a/SplitBit Programming Manual.md b/SplitBit Programming Manual.md index 2acdbb0..e443043 100644 --- a/SplitBit Programming Manual.md +++ b/SplitBit Programming Manual.md @@ -67,35 +67,45 @@ The Bytes column is the total length of the instruction, counting its opcode, an ### Arithmetic and Logic Operations: 9 Instructions | Hex Code | Mnemonic | Bytes | Description | | -- | ---- | -- | -- | -| 00 | ADD | 1 | Adds A, B, and the Carry Flag, the result is stored in Q. | -| 01 | SUB | 1 | Subtracts B and the Carry Flag from A, the result is stored in Q. | -| 02 | AND | 1 | Bitwise and of A and B, the result is stored in Q. | -| 03 | OR | 1 | Bitwise or of A and B, the result is stored in Q. | -| 04 | XOR | 1 | Bitwise xor of A and B, the result is stored in Q. | -| 05 | NOTA | 1 | Bitwise inversion of A, the result is stored in Q. | -| 06 | NOTB | 1 | Bitwise inversion of B, the result is stored in Q. | -| 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. | +| 10 | ADD | 1 | Adds A, B, and the Carry Flag, the result is stored in Q. | +| 11 | SUB | 1 | Subtracts B and the Carry Flag from A, the result is stored in Q. | +| 12 | AND | 1 | Bitwise and of A and B, the result is stored in Q. | +| 13 | OR | 1 | Bitwise or of A and B, the result is stored in Q. | +| 14 | XOR | 1 | Bitwise xor of A and B, the result is stored in Q. | +| 15 | NOTA | 1 | Bitwise inversion of A, the result is stored in Q. | +| 16 | NOTB | 1 | Bitwise inversion of B, the result is stored in Q. | +| 17 | SHL | 1 | A and B form a circular shift register. Rotate this register left. | +| 18 | SHR | 1 | A and B form a circular shift register. Rotate this register right. | -### Branch and Subroutine Operations: 16 Instructions +### Branch Operations: 10 Instructions | 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. | -| 11 | BRQ | 3 | Branch on Q. If Q is zero, loads the immediate next two bytes of Program Memory into the Program Counter. | -| 12 | BRA | 3 | Branch on A. If A is zero, loads the immediate next two bytes of Program Memory into the Program Counter. | -| 13 | BRB | 3 | Branch on B. If B is zero, loads the immediate next two bytes of Program Memory into the Program Counter. | -| 14 | BRC | 3 | Branch if Carry is set. | -| 15 | BRD | 2 | Branch to the address held in the named Data Pointer. | -| 1A | BNQ | 3 | Branch if Q is not zero. | -| 1B | BNA | 3 | Branch if A is not zero. | -| 1C | BNB | 3 | Branch if B is not zero. | -| 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. | -| 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. | -| 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. | +| 60 | 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. | +| 61 | BRQ | 3 | Branch on Q. If Q is zero, loads the immediate next two bytes of Program Memory into the Program Counter. | +| 62 | BRA | 3 | Branch on A. If A is zero, loads the immediate next two bytes of Program Memory into the Program Counter. | +| 63 | BRB | 3 | Branch on B. If B is zero, loads the immediate next two bytes of Program Memory into the Program Counter. | +| 64 | BRC | 3 | Branch if Carry is set. | +| 65 | BRD | 2 | Branch to the address held in the named Data Pointer. | +| 66 | BNQ | 3 | Branch if Q is not zero. | +| 67 | BNA | 3 | Branch if A is not zero. | +| 68 | BNB | 3 | Branch if B is not zero. | +| 69 | BNC | 3 | Branch if the Carry Flag is clear. | + +### Subroutine Operations: 6 Instructions + +A block of their own, because the branches and these outgrew one nibble between them. Each +raw form sits immediately below the ordinary one it cannot be mixed with - RCAL under CALL, +RRET under RET - since the frames differ and returning through the wrong one takes the +machine somewhere nobody named. + +| Hex Code | Mnemonic | Bytes | Description | +| -- | ---- | -- | -- | +| 70 | 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. | +| 71 | 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. | +| 72 | SWI | 2 | Software Interrupt. The next byte names a software vector. Pushes an interrupt frame and dispatches through it. Never masked. | +| 73 | RETI | 1 | Return from an interrupt. Restores everything the frame holds and carries on from where the interrupt arrived. | +| 74 | RRET | 1 | Return from a raw call. Takes back the Program Counter and nothing else. | +| 75 | 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 | Hex Code | Mnemonic | Bytes | Description | diff --git a/Tests/expected/cosmosMonitor.out b/Tests/expected/cosmosMonitor.out index 7dcf55f..d00ceae 100644 --- a/Tests/expected/cosmosMonitor.out +++ b/Tests/expected/cosmosMonitor.out @@ -5,16 +5,16 @@ CosmOS * loaded, starting at 4000 * bank 00 * 4000 47 00 20 00 SETD.0 2000 -4004 18 10 SWI 10 +4004 72 10 SWI 10 4006 47 00 20 44 SETD.0 2044 -400A 18 10 SWI 10 +400A 72 10 SWI 10 400C 47 00 20 7A SETD.0 207A 4010 27 1F INIB 1F -4012 18 11 SWI 11 +4012 72 11 SWI 11 4014 47 00 20 5D SETD.0 205D -* 4000 47 00 20 00 18 10 47 00 20 44 18 10 47 00 20 7A G. ...G. D..G. z -4010 27 1F 18 11 47 00 20 5D 18 10 47 00 20 7A 18 10 '...G. ]..G. z.. -4020 47 00 20 65 18 10 18 12 00 00 00 00 00 00 00 00 G. e............ +* 4000 47 00 20 00 72 10 47 00 20 44 72 10 47 00 20 7A G. .r.G. Dr.G. z +4010 27 1F 72 11 47 00 20 5D 72 10 47 00 20 7A 72 10 '.r.G. ]r.G. zr. +4020 47 00 20 65 72 10 72 12 00 00 00 00 00 00 00 00 G. er.r......... 4030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * bank 01 * 2000 61 20 70 72 6F 67 72 61 6D 2C 20 6C 6F 61 64 65 a program, loade @@ -32,43 +32,11 @@ CosmOS 8002 D1 00 OUTA 00 8004 26 0A INIA 0A 8006 D1 00 OUTA 00 -8008 18 12 SWI 12 -800A 00 ADD -800B 00 ADD -800C 00 ADD -* H -finished -* 8000 26 48 INIA 48 -8002 D1 00 OUTA 00 -8004 26 0A INIA 0A -8006 D1 00 OUTA 00 -8008 18 12 SWI 12 -800A 00 ADD -800B 00 ADD -800C 00 ADD -* bank 01 -* * bank 00 -* 8200: 8204: 8206: 8208: 820B: no such instruction -820B: that one needs a value after it -820B: 820D: * 8200 47 00 81 00 SETD.0 8100 -8204 18 10 SWI 10 -8206 42 02 LDA.2 -8208 4A 00 01 LDD.0.1 -820B 18 12 SWI 12 -820D 00 ADD -820E 00 ADD -820F 00 ADD -* hello, typed -finished -* > greet.sbx 210 -hello.sbx 52 -Life.sbx 1409 -Snake.sbx 2167 -Keys.sbx 663 -Say.sbx 155 -Break.sbx 148 -notes.txt 21 -8 files -> halted +8008 18 SHR +8009 12 AND +800A 00 ? +800B 00 ? +* Fault: 0x00 at Program Address 0x800A is not an instruction. +H Execution halted. -[exit 0] +[exit 1] diff --git a/Tests/expected/runOffTest.out b/Tests/expected/runOffTest.out new file mode 100644 index 0000000..a0f7f18 --- /dev/null +++ b/Tests/expected/runOffTest.out @@ -0,0 +1,3 @@ +Fault: 0x00 at Program Address 0x0004 is not an instruction. +.Execution halted. +[exit 1] diff --git a/Tests/instructiontable.py b/Tests/instructiontable.py index 64067d2..018f0f8 100755 --- a/Tests/instructiontable.py +++ b/Tests/instructiontable.py @@ -14,10 +14,11 @@ Shapes are what follows the opcode: import re import sys -ADDRESS = {0x10, 0x11, 0x12, 0x13, 0x14, 0x16, 0x17, 0x1A, 0x1B, 0x1C, 0x1D} -ONE_BYTE = {0x18, 0x26, 0x27} +# 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 = {0x15, 0x33, 0x36, 0x40, 0x41, 0x42, 0x43, 0x44, +SELECTOR = {0x65, 0x33, 0x36, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51} diff --git a/Tests/manifest b/Tests/manifest index 52a687f..1718a1c 100644 --- a/Tests/manifest +++ b/Tests/manifest @@ -656,6 +656,10 @@ stringKeyword | testPrograms/stringKeyword.asm | run | - # 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 | - | - +# Falling off the end of a program into Program Memory nobody wrote. It faults on the byte +# after the last instruction rather than executing a run of additions and going wrong +# somewhere else, which is what 0x00 through 0x0F being empty buys. +runOffTest | testPrograms/runOffTest.asm | run | - | - # WAIT, the seventy first instruction, on a disk given ten thousand cycles of latency. Two # reads with interrupts masked and no handler anywhere: the machine sleeps through both and