CALL and RET instructions.
Changed PSHP and POPP into CALL and RET, streamlining their use for subroutine calls. Updated Assembler and Emulator. New demonstration programs.
This commit is contained in:
@@ -0,0 +1,60 @@
|
|||||||
|
; print.asm
|
||||||
|
|
||||||
|
; Some simple SplitBit subroutines for printing.
|
||||||
|
; Anachronaut
|
||||||
|
; 10/27/2024
|
||||||
|
|
||||||
|
#Program
|
||||||
|
BRI Start ; Branch immediately to the start of the program.
|
||||||
|
|
||||||
|
printString:
|
||||||
|
CALL saveRegisters ; Store the registers so we can restore them later.
|
||||||
|
printLoop:
|
||||||
|
LDA ; Move the first character of the string into A.
|
||||||
|
BRA Return ; If A is NULL, the string is finished, so return.
|
||||||
|
OUTA 0x00 ; Output the character.
|
||||||
|
INCD ; Increment Data Pointer to the next character.
|
||||||
|
BRI printLoop ; Branch to the beginning of the loop.
|
||||||
|
|
||||||
|
lineFeed:
|
||||||
|
CALL saveRegisters ; Save the registers so they can be restored.
|
||||||
|
INIA 0x0A ; Load the character for linefeed into A.
|
||||||
|
OUTA 0x00 ; Output it.
|
||||||
|
CALL restoreRegisters
|
||||||
|
BRI Return ; We're done.
|
||||||
|
|
||||||
|
saveRegisters: ; Subroutine for storing the registers.
|
||||||
|
PSHD ; Store the Data Pointer, push it to the stack.
|
||||||
|
SETD Registers ; Set the Data Pointer to the address for the registers.
|
||||||
|
STQ ; Store Q.
|
||||||
|
INCD ; Move to the next address.
|
||||||
|
STA ; Store A.
|
||||||
|
INCD ; Move to the next address.
|
||||||
|
STB ; Store B.
|
||||||
|
INCD ; Move to the next address.
|
||||||
|
POPD ; Restore the Data Pointer.
|
||||||
|
RET ; Return to the caller.
|
||||||
|
|
||||||
|
restoreRegisters:
|
||||||
|
PSHD ; Save the Data Pointer.
|
||||||
|
SETD Registers ; Set the Data Pointer to the registers address.
|
||||||
|
RSTB ; Set B to 0.
|
||||||
|
LDA ; Load the value for Q into A.
|
||||||
|
ADD ; Add 0 to move the value to Q.
|
||||||
|
INCD ; Move to the next register value.
|
||||||
|
LDA ; Load A with its value.
|
||||||
|
INCD ; Move to the next register value.
|
||||||
|
LDB ; Load B with its value.
|
||||||
|
POPD ; Restore the Data Pointer.
|
||||||
|
RET ; Return to the caller.
|
||||||
|
|
||||||
|
Return:
|
||||||
|
CALL restoreRegisters ; Restore the registers.
|
||||||
|
RET ; Return to the caller.
|
||||||
|
|
||||||
|
#Data
|
||||||
|
|
||||||
|
Registers: ; This is where saveRegisters stores the registers temporarily.
|
||||||
|
0x00 ; Q Register
|
||||||
|
0x00 ; A Register
|
||||||
|
0x00 ; B Register
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
; printHello.asm
|
||||||
|
|
||||||
|
; As a demonstration of the new print routines, here's a new implementation for hello.asm
|
||||||
|
; Anachronaut
|
||||||
|
; 10/27/2024
|
||||||
|
|
||||||
|
; Include the subroutine file.
|
||||||
|
#Include print.asm
|
||||||
|
|
||||||
|
#Program
|
||||||
|
Start:
|
||||||
|
SETD myString ; Set the Data Pointer to the string we want to print.
|
||||||
|
CALL printString ; Call the subroutine.
|
||||||
|
CALL lineFeed ; Call the linefeed subroutine.
|
||||||
|
HALT ; Stop the CPU.
|
||||||
|
|
||||||
|
#Data
|
||||||
|
|
||||||
|
myString:
|
||||||
|
"Hello, World!"
|
||||||
@@ -71,11 +71,14 @@ void assemblerCleanup(intermediateElement *intermediateArray, int arraySize, cha
|
|||||||
}
|
}
|
||||||
// Free the intermediateArray itself.
|
// Free the intermediateArray itself.
|
||||||
free(intermediateArray);
|
free(intermediateArray);
|
||||||
|
|
||||||
// Free the list of included files.
|
// Free the list of included files.
|
||||||
freeIncludeList();
|
freeIncludeList();
|
||||||
|
|
||||||
// Free the list of labels.
|
// Free the list of labels.
|
||||||
freeLabelList();
|
freeLabelList();
|
||||||
// Free the output file name.
|
|
||||||
|
// Free the output file name
|
||||||
free(outputFileName);
|
free(outputFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ Instruction instruction_set[] = {
|
|||||||
{0x11, "BRQ"},
|
{0x11, "BRQ"},
|
||||||
{0x12, "BRA"},
|
{0x12, "BRA"},
|
||||||
{0x13, "BRB"},
|
{0x13, "BRB"},
|
||||||
|
{0x14, "CALL"},
|
||||||
|
{0x15, "RET"},
|
||||||
// Register Operations:
|
// Register Operations:
|
||||||
{0x20, "RSTA"},
|
{0x20, "RSTA"},
|
||||||
{0x21, "RSTB"},
|
{0x21, "RSTB"},
|
||||||
@@ -42,12 +44,10 @@ Instruction instruction_set[] = {
|
|||||||
{0x30, "PSHQ"},
|
{0x30, "PSHQ"},
|
||||||
{0x31, "PSHA"},
|
{0x31, "PSHA"},
|
||||||
{0x32, "PSHB"},
|
{0x32, "PSHB"},
|
||||||
{0x33, "PSHP"},
|
{0x33, "PSHD"},
|
||||||
{0x34, "PSHD"},
|
{0x34, "POPA"},
|
||||||
{0x35, "POPA"},
|
{0x35, "POPB"},
|
||||||
{0x36, "POPB"},
|
{0x36, "POPD"},
|
||||||
{0x37, "POPP"},
|
|
||||||
{0x38, "POPD"},
|
|
||||||
// Data Operations:
|
// Data Operations:
|
||||||
{0x40, "INCD"},
|
{0x40, "INCD"},
|
||||||
{0x41, "DECD"},
|
{0x41, "DECD"},
|
||||||
|
|||||||
@@ -112,9 +112,9 @@ void populateOutputBuffers(intermediateElement *intermediateArray, int arraySize
|
|||||||
Program[(*programCount)++] = intermediateArray[i].byteValue;
|
Program[(*programCount)++] = intermediateArray[i].byteValue;
|
||||||
|
|
||||||
// Check if it's a branch instruction.
|
// Check if it's a branch instruction.
|
||||||
if ((intermediateArray[i].byteValue & 0xF0) == 0x10) {
|
if (((intermediateArray[i].byteValue & 0xF0) == 0x10) && (intermediateArray[i].byteValue != 0x15)){
|
||||||
if (intermediateArray[i + 1].type != LABEL) {
|
if (intermediateArray[i + 1].type != LABEL) {
|
||||||
fprintf(stderr, "Error: Branch without label.\n");
|
fprintf(stderr, RED "Error: Branch without label.\n" RESET);
|
||||||
printf("File: %s at line %d.\n", intermediateArray[i].fileName, intermediateArray[i].lineNumber);
|
printf("File: %s at line %d.\n", intermediateArray[i].fileName, intermediateArray[i].lineNumber);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-19
@@ -114,6 +114,24 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) {
|
|||||||
cpu->ProgramCounter+=2;
|
cpu->ProgramCounter+=2;
|
||||||
}
|
}
|
||||||
break;
|
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--;
|
||||||
|
genericBranch(cpu);
|
||||||
|
break;
|
||||||
|
case 0x15:
|
||||||
|
// RET - Return from subroutine, restore the Program Counter from the Stack.
|
||||||
|
// POPP - Pop Data to the Program Counter
|
||||||
|
cpu->StackPointer++;
|
||||||
|
cpu->ProgramCounter = (uint16_t)cpu->Data[cpu->StackPointer] << 8;
|
||||||
|
cpu->StackPointer++;
|
||||||
|
cpu->ProgramCounter = cpu->ProgramCounter | (uint16_t)cpu->Data[cpu->StackPointer];
|
||||||
|
cpu->ProgramCounter += 2; // Because it needs to skip over the address when it returns.
|
||||||
|
break;
|
||||||
//
|
//
|
||||||
// 2x - Register Operations:
|
// 2x - Register Operations:
|
||||||
//
|
//
|
||||||
@@ -177,15 +195,8 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) {
|
|||||||
cpu->Data[cpu->StackPointer] = cpu->B;
|
cpu->Data[cpu->StackPointer] = cpu->B;
|
||||||
cpu->StackPointer--;
|
cpu->StackPointer--;
|
||||||
break;
|
break;
|
||||||
case 0x33:
|
|
||||||
// PSHP - Push the Program Counter to the Stack.
|
|
||||||
// Order, low byte, high byte
|
|
||||||
cpu->Data[cpu->StackPointer] = cpu->ProgramCounter & 0xFF;
|
|
||||||
cpu->StackPointer--;
|
|
||||||
cpu->Data[cpu->StackPointer] = (cpu->ProgramCounter >> 8) & 0xFF;
|
|
||||||
cpu->StackPointer--;
|
|
||||||
break;
|
break;
|
||||||
case 0x34:
|
case 0x33:
|
||||||
// PSHD - Push the Data Pointer to the Stack.
|
// PSHD - Push the Data Pointer to the Stack.
|
||||||
// Order, low byte, high byte
|
// Order, low byte, high byte
|
||||||
cpu->Data[cpu->StackPointer] = cpu->DataPointer & 0xFF;
|
cpu->Data[cpu->StackPointer] = cpu->DataPointer & 0xFF;
|
||||||
@@ -193,26 +204,18 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) {
|
|||||||
cpu->Data[cpu->StackPointer] = (cpu->DataPointer >> 8) & 0xFF;
|
cpu->Data[cpu->StackPointer] = (cpu->DataPointer >> 8) & 0xFF;
|
||||||
cpu->StackPointer--;
|
cpu->StackPointer--;
|
||||||
break;
|
break;
|
||||||
case 0x35:
|
case 0x34:
|
||||||
// POPA - Pop Data to A.
|
// POPA - Pop Data to A.
|
||||||
cpu->StackPointer++;
|
cpu->StackPointer++;
|
||||||
cpu->A = cpu->Data[cpu->StackPointer];
|
cpu->A = cpu->Data[cpu->StackPointer];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 0x36:
|
case 0x35:
|
||||||
// POPB - Pop Data to B.
|
// POPB - Pop Data to B.
|
||||||
cpu->StackPointer++;
|
cpu->StackPointer++;
|
||||||
cpu->B = cpu->Data[cpu->StackPointer];
|
cpu->B = cpu->Data[cpu->StackPointer];
|
||||||
break;
|
break;
|
||||||
case 0x37:
|
case 0x36:
|
||||||
// POPP - Pop Data to the Program Counter
|
|
||||||
cpu->StackPointer++;
|
|
||||||
cpu->ProgramCounter = (uint16_t)cpu->Data[cpu->StackPointer] << 8;
|
|
||||||
cpu->StackPointer++;
|
|
||||||
cpu->ProgramCounter = cpu->ProgramCounter | (uint16_t)cpu->Data[cpu->StackPointer];
|
|
||||||
cpu->ProgramCounter += 3; // Because it needs to skip over the subsequent branch instruction on return.
|
|
||||||
break;
|
|
||||||
case 0x38:
|
|
||||||
// POPD - Pop Data to the Data Pointer
|
// POPD - Pop Data to the Data Pointer
|
||||||
cpu->StackPointer++;
|
cpu->StackPointer++;
|
||||||
cpu->DataPointer = (uint16_t)cpu->Data[cpu->StackPointer] << 8;
|
cpu->DataPointer = (uint16_t)cpu->Data[cpu->StackPointer] << 8;
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ 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.
|
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.
|
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.
|
13 | BRB | Branch on B. If B is zero, loads the immediate next two bytes of Program Memory into the Program Counter.
|
||||||
|
14 | CALL | Push the Program Counter to the Stack, loads the immediate next two bytes of Program Memory into the Program Counter.
|
||||||
|
15 | RET | Restores the Program Counter from the Stack, used to return from subroutines.
|
||||||
|
|
||||||
### Register Operations: 6 Instructions
|
### Register Operations: 6 Instructions
|
||||||
| Hex Code | Mnemonic | Description |
|
| Hex Code | Mnemonic | Description |
|
||||||
@@ -57,12 +59,10 @@ Hex Code | Mnemonic | Description
|
|||||||
30 | PSHQ | Stores Q into Data Memory at the location referenced by the Stack Pointer then decrements the Stack Pointer.
|
30 | PSHQ | Stores Q into Data Memory at the location referenced by the Stack Pointer then decrements the Stack Pointer.
|
||||||
31 | PSHA | Stores A into Data Memory at the location referenced by the Stack Pointer then decrements the Stack Pointer.
|
31 | PSHA | Stores A into Data Memory at the location referenced by the Stack Pointer then decrements the Stack Pointer.
|
||||||
32 | PSHB | Stores B into Data Memory at the location referenced by the Stack Pointer then decrements the Stack Pointer.
|
32 | PSHB | Stores B into Data Memory at the location referenced by the Stack Pointer then decrements the Stack Pointer.
|
||||||
33 | PSHP | Stores the Program Counter to the next two bytes in the stack, decrements the Stack Pointer by two.
|
33 | PSHD | Stores the Data Pointer to the next two bytes in the stack, decrements the Stack Pointer by two.
|
||||||
34 | PSHD | Stores the Data Pointer to the next two bytes in the stack, decrements the Stack Pointer by two.
|
34 | POPA | Reads the location referenced by the Stack Pointer from Data Memory into A then increments the Stack Pointer.
|
||||||
35 | POPA | Reads the location referenced by the Stack Pointer from Data Memory into A then increments the Stack Pointer.
|
35 | POPB | Reads the location referenced by the Stack Pointer from Data Memory into B then increments the Stack Pointer.
|
||||||
36 | POPB | Reads the location referenced by the Stack Pointer from Data Memory into B then increments the Stack Pointer.
|
36 | POPD | Restores the Program Counter from the top two bytes in the stack, increments the Stack Pointer by two.
|
||||||
37 | POPP | Restores the Program Counter from the top two bytes in the stack, increments the Stack Pointer by two.
|
|
||||||
38 | POPD | Restores the Program Counter from the top two bytes in the stack, increments the Stack Pointer by two.
|
|
||||||
|
|
||||||
### Data Operations: 7 Instructions
|
### Data Operations: 7 Instructions
|
||||||
Hex Code | Mnemonic | Description
|
Hex Code | Mnemonic | Description
|
||||||
@@ -103,26 +103,38 @@ The current implementation has Input 0 and Output 0 hooked to stdout and stdin r
|
|||||||
|
|
||||||
### Example Program: Hello World
|
### Example Program: Hello World
|
||||||
```
|
```
|
||||||
; This is a basic hello world program for the SplitBit CPU.
|
; Hello World for SplitBit CPU
|
||||||
; We'll create a loop that outputs each byte of our string to Output 0, the text console.
|
; First, we define the string in Data Memory.
|
||||||
|
Data:
|
||||||
|
0000 0x48 ; 'H'
|
||||||
|
0001 0x65 ; 'e'
|
||||||
|
0002 0x6C ; 'l'
|
||||||
|
0003 0x6C ; 'l'
|
||||||
|
0004 0x6F ; 'o'
|
||||||
|
0005 0x2C ; ','
|
||||||
|
0006 0x20 ; ' '
|
||||||
|
0007 0x57 ; 'W'
|
||||||
|
0008 0x6F ; 'o'
|
||||||
|
0009 0x72 ; 'r'
|
||||||
|
000A 0x6C ; 'l'
|
||||||
|
000B 0x64 ; 'd'
|
||||||
|
000C 0x32 ; '!'
|
||||||
|
000D 0x0A ; This is a linefeed, it's equivalent to putting '\n' in a string in C.
|
||||||
|
000E 0x00 ; Zero terminates the string.
|
||||||
|
|
||||||
#Program
|
; Next, we'll create a loop that outputs each byte of our string to Output 0, the text console.
|
||||||
|
Program:
|
||||||
Start:
|
0000 LDA 0x42 ; Load the first byte of the string into A.
|
||||||
LDA ; Load a byte of the string into A.
|
0001 BRA 0x12 ; If A is zero, branch out of the loop.
|
||||||
BRA End ; If A is zero, branch out of the loop.
|
0002 0x00 0x00 ; The high byte of the branch.
|
||||||
OUTA 0x00 ; Output the value in A to Port 0, the text console.
|
0003 0x0A 0x0A ; The low byte of the branch.
|
||||||
INCD ; Increment the Data Pointer to the next byte of the string.
|
0004 OUTA 0xD1 ; Output the value in A.
|
||||||
BRI Start ; Branch immediately to the start of the loop.
|
0005 0x01 0x00 ; The Output Port to use.
|
||||||
|
0006 INCD 0x40 ; Increment the Data Pointer to the next byte of the string.
|
||||||
End:
|
0007 BRI 0x10 ; Branch immediately to the start of the loop.
|
||||||
INIA 0x0A ; We'll load a linefeed into A and output it to make it look nice.
|
0008 0x00 0x00 ; The high byte of the branch address.
|
||||||
OUTA 0x00 ; Output it to the text console.
|
0009 0x00 0x00 ; The low byte of the branch address.
|
||||||
HALT ; Terminate the program.
|
000A HALT 0xFF ; The end of the program.
|
||||||
|
|
||||||
#Data
|
|
||||||
|
|
||||||
"Hello, World!"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Structure of a SplitBit Binary File:
|
### Structure of a SplitBit Binary File:
|
||||||
@@ -131,5 +143,5 @@ The Program and Data values are both stored in a single file for loading into th
|
|||||||
Here's an example hex dump of the hello world program stored in the proper format:
|
Here's an example hex dump of the hello world program stored in the proper format:
|
||||||
|
|
||||||
```
|
```
|
||||||
50 52 47 00 0F 26 12 00 0A D1 00 40 10 00 00 28 0A D1 00 FF 44 41 54 00 0E 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 00
|
50 52 47 00 0A 42 12 00 0A D1 00 40 10 00 00 FF 44 41 54 00 0E 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A 00
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user