From 228cf0593afb635f827cf22b889173acb34cef48 Mon Sep 17 00:00:00 2001 From: Anachronaut <75824887+RealBusinessAccount@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:30:15 -0400 Subject: [PATCH] Added stuff. Fixed bug. Data Pointer value now properly displayed in debug mode. Added conditional calls to match conditional branches. --- Programs/Libraries/print.asm | 128 +++++++++++++++++++++ Programs/printHello.asm | 6 +- Programs/testPrograms/printDecimalTest.asm | 25 ++++ Programs/testPrograms/printDigitTest.asm | 17 +++ Programs/testPrograms/printHexTest.asm | 22 ++++ Programs/testPrograms/printTest.asm | 58 ++++++++++ Programs/testPrograms/shiftTest.asm | 84 ++++++++++++++ Source/Assembler/assembly.c | 10 +- Source/Assembler/secondPass.c | 2 +- Source/Emulator/cpu.c | 91 +++++++++------ Source/Emulator/utility.c | 2 +- SplitBit Programming Manual.md | 13 ++- 12 files changed, 414 insertions(+), 44 deletions(-) create mode 100644 Programs/Libraries/print.asm create mode 100644 Programs/testPrograms/printDecimalTest.asm create mode 100644 Programs/testPrograms/printDigitTest.asm create mode 100644 Programs/testPrograms/printHexTest.asm create mode 100644 Programs/testPrograms/printTest.asm create mode 100644 Programs/testPrograms/shiftTest.asm diff --git a/Programs/Libraries/print.asm b/Programs/Libraries/print.asm new file mode 100644 index 0000000..4463010 --- /dev/null +++ b/Programs/Libraries/print.asm @@ -0,0 +1,128 @@ +; print.asm + +; Some simple SplitBit subroutines for printing. +; Anachronaut +; 10/27/2024 + +#Program + BRI start ; Branch immediately to the start of the program. + +lineFeed: + INIA 0x0A ; Load the character for linefeed into A. + OUTA 0x00 ; Output it. + RET ; Return to the caller. + +; Expects A to contain the number of spaces to print. +blankSpace: + INIB 0x20 ; Set B to the ASCII value for space. + OUTB 0x00 ; Print it. + BRA printDone ; If A is zero, we're done. + DECA ; Decrement A. + BRI blankSpace ; Branch back to the loop again. + +printString: ; Expects Data Pointer to be set to the beginning of the string to be printed. + LDA ; Move the first character of the string into A. + BRA printDone ; If A is NULL, the string is finished, so return. + OUTA 0x00 ; Output the character. + INCD ; Increment Data Pointer to the next character. + BRI printString ; Branch to the beginning of the loop. + printDone: + RET ; Return to the caller. + +; Expects A to contain the value to be printed in decimal form. +printByteDecimal: + ; Clear the buffer we're going to write into. + SETD DecimalValue ; Set the Data Pointer to the buffer. + RSTB ; Set B to 0. + STB ; Store it in the buffer. + INCD ; Increment to the tens place. + STB ; Store 0 in it. + INCD ; Increment to the hundred's place. + STB ; Store 0 in it. + INIB 0d10 ; Load 10 into B. + PSHA ; Save the value onto the stack. + printDecimalLoopStart: + POPA ; Pop the value to A from the stack. + CCF ; Clear the Carry Flag. + SUB ; Subtract 10 from the value in A. + BRC getOnes ; If 10 is bigger than A, We're done looping and A contains the ones. + BRA getOnes ; If the value 0, we're also done. + ; Otherwise, increment the tens place. + SETD DecimalValue ; Set the Data Pointer to the buffer. + INCD ; Increment to the tens place. + PSHQ ; Save the new value onto the stack. + LDA ; Load the number of tens into A. + INCA ; Increment it. + ; Now check to see if there are ten tens and we need to carry to the hundreds. + CCF ; Clear the Carry Flag. + SUB ; Subtract 10 from A. + BRQ incrementHundreds ; If Q is 0, set the tens to 0 and increment the hundreds place. + STA ; Otherwise, store the value back to the tens place. + BRI printDecimalLoopStart ; and loop again. + incrementHundreds: + STQ ; Store 0 in the tens place. + INCD ; Increment the Data Pointer to the hundreds place. + LDA ; Load it into A. + INCA ; Increment it. + STA ; Store it back again. + BRI printDecimalLoopStart ; and Loop again. + getOnes: + SETD DecimalValue ; Set the Data Pointer to the ones place. + STA ; Store A in it. + ; At this point we're done. We just need to print the digits with the printDecimalDigit subroutine. + INCD INCD ; Increment the Data Pointer twice to the hundreds place. + decimalCheckLoop: + LDA ; Load the value into A. + BRA checkTens ; If the hundreds are 0, skip printing their digit. + CALL printDecimalDigit ; Otherwise, print it. + DECD + LDA + CALL printDecimalDigit ; And the tens place, too. + BRI printOnes ; And finally, the ones. + checkTens: + DECD ; Decrement to the tens place. + LDA ; Load it into A. + BRA printOnes ; If the tens are zero, skip printing their digit, too. + CALL printDecimalDigit ; Otherwise, print it. + printOnes: + DECD ; Decrement to the ones place. + LDA + CALL printDecimalDigit ; Print it no matter what. + RET ; We're done, return to the caller. + +; Expects A to contain the byte you want to print as a hexadecimal value. +printByteHex: + INIB 0x00 ; Set B to 0. + SHR SHR SHR SHR ; Shift Right four times to move the high nybble into the lower half of A. + CALL printHexDigit ; Print the nybble. + INIA 0x00 ; Set A to 0. + SHL SHL SHL SHL ; Shift left four times to move the low nybble back into the lower half of A. + CALL printHexDigit ; Print the nybble. + RET ; Return to the caller. + +; Expects A to contain the nybble you want to print. +printHexDigit: + INIB 0d10 ; Set B to 10. + SUB ; Subtract it. + BRC printDecimalDigit ; If the Carry Flag is set, the digit is less than 9. Print it. + ; Since it's greater than 9, add the ASCII offset for capital letters. + INIB 0x37 ; Set B to the offset. + ADD ; Add it to A. + OUTQ 0x00 ; Print the result. + RET ; Return to the caller. + +; Expects A to contain a single BCD digit to print. +printDecimalDigit: + INIB 0x30 ; Load ASCII offset for numbers into B. + CCF ; Clear the Carry Flag. + ADD ; Add it to A. + OUTQ 0x00 ; Send it to the output. + RET ; Return from subroutine. + +#Data + +; This is where printDecimal stores its result. +DecimalValue: + 0x00 ; The ones place. + 0x00 ; The tens place. + 0x00 ; The hundreds place. diff --git a/Programs/printHello.asm b/Programs/printHello.asm index 452dda5..f0bbfca 100644 --- a/Programs/printHello.asm +++ b/Programs/printHello.asm @@ -5,7 +5,7 @@ ; 10/27/2024 ; Include the subroutine file. -#Include print.asm +#Include Libraries/print.asm #Program @@ -14,12 +14,12 @@ start: CALL printString ; Call the string printing subroutine. CALL lineFeed ; Call the line feed subroutine to end the line. SETD MyDecimal ; Set the Data Pointer to the value to print as a decimal. - CALL printDecimal ; Call the decimal printing subroutine. + LDA ; Load it into A. + CALL printByteDecimal ; Call the decimal printing subroutine. SETD MyMessage ; Set the Data Pointer to another string. CALL printString ; Call the string printing subroutine again. CALL lineFeed ; Call the line feed subroutine to end the line. HALT ; Stop the program. - #Data MyString: diff --git a/Programs/testPrograms/printDecimalTest.asm b/Programs/testPrograms/printDecimalTest.asm new file mode 100644 index 0000000..cffdf98 --- /dev/null +++ b/Programs/testPrograms/printDecimalTest.asm @@ -0,0 +1,25 @@ +; Tests for the printDecimal subroutine. + +#Program + +printByteDecimalTest: + INIB 0xFF ; Load 255 into B. + printDecimalTestloop: + SUB + CCF ; Clear the Carry Flag. + CALL printByteDecimal ; Print the value. + INCA ; Increment it. + PSHA + INIA 0x00 + CALL blankSpace + POPA + BRQ printDecimalTestend ; If Q is zero, we're done. + BRI printDecimalTestloop ; Branch back to the start of the loop. + printDecimalTestend: + CALL lineFeed + RET + +#Data + +Space: + " " diff --git a/Programs/testPrograms/printDigitTest.asm b/Programs/testPrograms/printDigitTest.asm new file mode 100644 index 0000000..b36c0fa --- /dev/null +++ b/Programs/testPrograms/printDigitTest.asm @@ -0,0 +1,17 @@ +#Program + +printDigitTest: + INIB 0d10 + printDigitTestLoop: + CALL printDecimalDigit + INCA + SUB + CCF + BRQ printDigitTestend + CALL lineFeed + BRI printDigitTestLoop + printDigitTestend: + CALL lineFeed + RET + +#Data diff --git a/Programs/testPrograms/printHexTest.asm b/Programs/testPrograms/printHexTest.asm new file mode 100644 index 0000000..650f9bb --- /dev/null +++ b/Programs/testPrograms/printHexTest.asm @@ -0,0 +1,22 @@ +; Test for the hexadecimal printing routines. + +#Program + printByteHexTest: + INIB 0xFF + CALL printByteHex + printHexTestLoop: + PSHA + INIA 0x00 + CALL blankSpace + POPA + INCA + SUB + CCF + CALL printByteHex + BRQ printHexTestend + BRI printHexTestLoop + printHexTestend: + CALL lineFeed + RET + +#Data diff --git a/Programs/testPrograms/printTest.asm b/Programs/testPrograms/printTest.asm new file mode 100644 index 0000000..8e7e9f0 --- /dev/null +++ b/Programs/testPrograms/printTest.asm @@ -0,0 +1,58 @@ +; Tests for the printing subroutines provided by print.asm + +#Include ../Libraries/print.asm +#Include printDigitTest.asm +#Include printDecimalTest.asm +#Include printHexTest.asm + +#Program + +clearRegisters: + INIA 0x00; + INIB 0x00; + ADD + RET + +start: + SETD String0 + CALL printString + CALL lineFeed + SETD String1 + CALL printString + CALL lineFeed + CALL clearRegisters + CALL printDigitTest + CALL lineFeed + SETD String2 + CALL printString + CALL lineFeed + CALL clearRegisters + CALL printByteDecimalTest + CALL lineFeed + SETD String3 + CALL printString + CALL lineFeed + CALL clearRegisters + CALL printByteHexTest + CALL lineFeed + SETD String4 + CALL printString + CALL lineFeed + HALT + +#Data + +String0: +"Test suite for SplitBit's print.asm library." + +String1: +"Testing printDigit..." + +String2: +"Testing printByteDecimal..." + +String3: +"Testing printByteHex..." + +String4: +"Testing complete!" diff --git a/Programs/testPrograms/shiftTest.asm b/Programs/testPrograms/shiftTest.asm new file mode 100644 index 0000000..970a976 --- /dev/null +++ b/Programs/testPrograms/shiftTest.asm @@ -0,0 +1,84 @@ +; A simple test for the new SHL and SHR instructions. + +#Include print.asm + +#Program + +start: + SETD String0 + CALL printString + loop0: + SETD TestValue0 + CALL printDecimal + LDA + SHL + STA + SETD Space + CALL printString + BRA doB + BRI loop0 + doB: + CALL lineFeed + SETD String1 + CALL printString + SETD TestValue0 + STB + loop1: + SETD TestValue0 + CALL printDecimal + LDB + SHL + STB + SETD Space + CALL printString + BRB doRight + BRA loop1 + doRight: + CALL lineFeed + SETD String0 + CALL printString + loop2: + SETD TestValue1 + CALL printDecimal + LDA + SHR + STA + SETD Space + CALL printString + BRA doRightB + BRI loop2 + doRightB: + CALL lineFeed + SETD String1 + CALL printString + SETD TestValue1 + STB + loop3: + SETD TestValue1 + CALL printDecimal + LDB + SHR + STB + SETD Space + CALL printString + BRB end + BRA loop3 + end: + CALL lineFeed + HALT + +#Data +TestValue0: + 0d01 + +TestValue1: + 0d128 + +String0: + "A values: " + +String1: + "B values: " + +Space: + " " diff --git a/Source/Assembler/assembly.c b/Source/Assembler/assembly.c index 769c2cd..22e0441 100644 --- a/Source/Assembler/assembly.c +++ b/Source/Assembler/assembly.c @@ -29,9 +29,13 @@ Instruction instruction_set[] = { {0x11, "BRQ"}, {0x12, "BRA"}, {0x13, "BRB"}, - {0x14, "CALL"}, - {0x15, "RET"}, - {0x16, "BRC"}, + {0x14, "BRC"}, + {0x17, "CALL"}, + {0x18, "CALLA"}, + {0x19, "CALLB"}, + {0x1A, "CALLQ"}, + {0x1B, "CALLCF"}, + {0x1F, "RET"}, // Register Operations: {0x20, "RSTA"}, {0x21, "RSTB"}, diff --git a/Source/Assembler/secondPass.c b/Source/Assembler/secondPass.c index 19ec427..07f568b 100644 --- a/Source/Assembler/secondPass.c +++ b/Source/Assembler/secondPass.c @@ -112,7 +112,7 @@ void populateOutputBuffers(intermediateElement *intermediateArray, int arraySize Program[(*programCount)++] = intermediateArray[i].byteValue; // Check if it's a branch instruction. - if (((intermediateArray[i].byteValue & 0xF0) == 0x10) && (intermediateArray[i].byteValue != 0x15)){ + if (((intermediateArray[i].byteValue & 0xF0) == 0x10) && (intermediateArray[i].byteValue != 0x1F)){ if (intermediateArray[i + 1].type != LABEL) { fprintf(stderr, RED "Error: Branch without label.\n" RESET); printf("File: %s at line %d.\n", intermediateArray[i].fileName, intermediateArray[i].lineNumber); diff --git a/Source/Emulator/cpu.c b/Source/Emulator/cpu.c index 9a877c0..ec1b919 100644 --- a/Source/Emulator/cpu.c +++ b/Source/Emulator/cpu.c @@ -31,6 +31,31 @@ void genericBranch(CPURegisters *cpu){ cpu->ProgramCounter = DestinationAddress-1; } +void genericCall(CPURegisters *cpu){ + // Order, low byte, high byte + cpu->Data[cpu->StackPointer] = cpu->ProgramCounter & 0xFF; + cpu->StackPointer--; + cpu->Data[cpu->StackPointer] = (cpu->ProgramCounter >> 8) & 0xFF; + cpu->StackPointer--; + // Push the Data Pointer to the Stack. + // Order, low byte, high byte + cpu->Data[cpu->StackPointer] = cpu->DataPointer & 0xFF; + cpu->StackPointer--; + cpu->Data[cpu->StackPointer] = (cpu->DataPointer >> 8) & 0xFF; + cpu->StackPointer--; + // Push Q to the Stack. + cpu->Data[cpu->StackPointer] = cpu->Q; + cpu->StackPointer--; + // Push B to the Stack. + cpu->Data[cpu->StackPointer] = cpu->B; + cpu->StackPointer--; + // Push A to the Stack. + cpu->Data[cpu->StackPointer] = cpu->A; + cpu->StackPointer--; + // Perform a Generic Branch to the Address. + genericBranch(cpu); +} + uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { switch(Instruction) { // 0x - Arithmetic and Logic Operations. @@ -131,31 +156,41 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { } break; case 0x14: - // CALL - Push the Program Counter to the Stack, and perform an immediate branch. - // Order, low byte, high byte - cpu->Data[cpu->StackPointer] = cpu->ProgramCounter & 0xFF; - cpu->StackPointer--; - cpu->Data[cpu->StackPointer] = (cpu->ProgramCounter >> 8) & 0xFF; - cpu->StackPointer--; - // Push the Data Pointer to the Stack. - // Order, low byte, high byte - cpu->Data[cpu->StackPointer] = cpu->DataPointer & 0xFF; - cpu->StackPointer--; - cpu->Data[cpu->StackPointer] = (cpu->DataPointer >> 8) & 0xFF; - cpu->StackPointer--; - // Push Q to the Stack. - cpu->Data[cpu->StackPointer] = cpu->Q; - cpu->StackPointer--; - // Push B to the Stack. - cpu->Data[cpu->StackPointer] = cpu->B; - cpu->StackPointer--; - // Push A to the Stack. - cpu->Data[cpu->StackPointer] = cpu->A; - cpu->StackPointer--; - // Perform a Generic Branch to the Address. - genericBranch(cpu); + // BRC - Do an immediate branch if the Carry Flag is set. + if (cpu->Status & 0x01) { + genericBranch(cpu); + } else { + cpu->ProgramCounter+=2; + } break; - case 0x15: + case 0x17: + // CALL - Push the Program Counter to the Stack, and perform an immediate branch. + genericCall(cpu); + break; + case 0x18: + // CALLA + if (cpu->A == 0) { + genericCall(cpu); + } + break; + case 0x19: + // CALLB + if (cpu->B == 0) { + genericCall(cpu); + } + break; + case 0x1A: + // CALLQ + if (cpu->Q == 0) { + genericCall(cpu); + } + case 0x1B: + // CALLCF + if (cpu->Status & 0x01) { + genericCall(cpu); + } + break; + case 0x1F: // RET - Return from subroutine, restore the registers and set the Program Counter to the Return Address. // Pop A from the Stack. cpu->StackPointer++; @@ -179,14 +214,6 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) { // Add 2 to the Program Counter to skip over the address when it returns. cpu->ProgramCounter += 2; break; - case 0x16: - // BRC - Do an immediate branch is the Carry Flag is set. - if (cpu->Status & 0x01) { - genericBranch(cpu); - } else { - cpu->ProgramCounter+=2; - } - break; // // 2x - Register Operations: // diff --git a/Source/Emulator/utility.c b/Source/Emulator/utility.c index 399b66f..b952a5a 100644 --- a/Source/Emulator/utility.c +++ b/Source/Emulator/utility.c @@ -51,6 +51,6 @@ void printRegisters(CPURegisters *cpu, uint8_t *Program, uint8_t *Data) { printf("A: 0x%02X\tB: 0x%02X\tQ: 0x%02X\tStatus: 0b%08b\n", cpu->A, cpu->B, cpu->Q, cpu->Status); printf("Program Counter: 0x%04X Current Instruction: 0x%02X (%s)\n", cpu->ProgramCounter, Program[cpu->ProgramCounter],getMnemonic(Program[cpu->ProgramCounter])); printf(" Data Pointer: 0x%04X Current Data Value: 0x%02X\n", cpu->DataPointer, Data[cpu->DataPointer]); - printf(" Stack Pointer: 0x%04X Current Value: (0x%02X) (0x%02X)\n", Data[cpu->StackPointer], Data[cpu->StackPointer+1], Data[cpu->StackPointer+2]); + printf(" Stack Pointer: 0x%04X Current Value: (0x%02X) (0x%02X)\n", cpu->StackPointer, Data[cpu->StackPointer+1], Data[cpu->StackPointer+2]); } diff --git a/SplitBit Programming Manual.md b/SplitBit Programming Manual.md index cf3bd5f..e623d3c 100644 --- a/SplitBit Programming Manual.md +++ b/SplitBit Programming Manual.md @@ -9,7 +9,7 @@ It has six registers: - The Program Counter is a 16 bit pointer into the Program Memory. It points to the current operation the CPU is executing. It initializes at address 0x0000. - The Data Pointer is a 16 bit pointer into the Data Memory. It points to the current byte of data that the CPU can read or write to, and can be set arbitrarily by the programmer to any value in the Data Memory. It initializes at location 0x0000. - The Stack Pointer is a 16 bit pointer into the Data Memory. It points to the current element of the stack, and is only ever modified by the use of the push and pop instructions. It initializes at location 0xFFFF. - - The Status register is an 8 bit register whose various bits are used as flags. Only three of these flags are used in the current implementation. + - The Status register is an 8 bit register whose various bits are used as flags. Only three of these flags are used in the current implementation. - Bit 0 is the Carry/Borrow Flag. Any arithmetic operation either sets or clears it depending on whether or not the result causes Q to overflow/underflow. It is a 1 if a carry/underflow occurred, and a 0 otherwise. - Bit 1 is the Stack Collision Flag. It is set if the Data Pointer's value ever meets or exceeds the Stack Pointer's value. This condition also sets the Halt Flag. - Bit 7 is the Halt Flag. It is set by the HALT instruction, or if there is a stack collision. @@ -38,9 +38,14 @@ Hex Code | Mnemonic | Description 11 | BRQ | Branch on Q. If Q is zero, loads the immediate next two bytes of Program Memory into the Program Counter. 12 | BRA | Branch on A. If A is zero, loads the immediate next two bytes of Program Memory into the Program Counter. 13 | BRB | Branch on B. If B is zero, loads the immediate next two bytes of Program Memory into the Program Counter. -14 | CALL | Stores all the registers to the Stack, A, B, Q, the Data Pointer, and the Program Counter, then performs an immediate branch. -15 | RET | Restores all registers from the Stack, then immediately branches to the Return Address by setting the Program Counter to the next instruction after the last CALL. -16 | BRC | Branch if Carry is set. +14 | BRC | Branch if Carry is set. +17 | CALL | Call subroutine. Stores all the registers to the Stack, A, B, Q, the Data Pointer, and the Program Counter, then performs an immediate branch. +18 | CALLA | Conditional Call on A. Calls subroutine if A is zero. +19 | CALLB | Conditional Call on B. Calls subroutine if B is zero. +1A | CALLQ | Conditional Call on Q. Calls subroutine if Q is zero. +1B | CALLCF | Conditional Call on Carry. Calls subroutine if the Carry Flag is set. +1F | RET | Restores all registers from the Stack, then immediately branches to the Return Address by setting the Program Counter to the next instruction after the last CALL. + ### Register Operations: 11 Instructions | Hex Code | Mnemonic | Description |