DP offset instructions, new demo programs.

Added DPUP and DPDN, which take a one byte operand specifying how far up or down to offset the Data Pointer.
Three Fibonacci generators using the print.asm library.
 - 8 bit values printing in decimal representation.
- 16 bit values printing in hexadecimal representation.
- 32 bit values printing in hexadecimal representation.
This commit is contained in:
Anachronaut
2024-11-01 12:13:13 -04:00
committed by GitHub
parent e5241beb51
commit f4c78e68ba
7 changed files with 332 additions and 90 deletions
+77
View File
@@ -0,0 +1,77 @@
; A Fibonacci number generating program that uses two bytes to store the value.
#Include Libraries/print.asm
#Program
start:
; Swap ValueB and ValueA.
; First, store ValueA on the stack.
SETD ValueA
LDA
PSHA
INCD
LDA
PSHA
; Now copy ValueB into AB.
SETD ValueB
LDA ; High byte
INCD
LDB ; Low byte
; Now save it back to ValueA
SETD ValueA
STA ; High byte
INCD
STB ; Low byte.
; Now retrieve value A from the stack and store it in ValueB.
POPB
POPA
SETD ValueB
STA
INCD
STB
; Print ValueA.
SETD ValueA
LDA
CALL printByteHex
INCD
LDA
CALL printByteHex
CALL blankSpace
; Now add ValueA and ValueB, and store the result in ValueA.
; Add the low bytes of ValueA and ValueB
SETD ValueB
INCD
LDA
SETD ValueA
INCD
LDB
CCF
ADD
; Store the result in ValueA.
STQ
; Now add the high bytes of ValueA and ValueB.
DECD
LDB
SETD ValueB
LDA
ADD
; If this addition overflows, we're done.
BRC end
; Otherwise, store the result in ValueA.
SETD ValueA
STQ
; And branch back to the beginning of the loop.
BRI start
end:
CALL lineFeed
HALT
#Data
ValueA:
; Low byte, high byte.
0x00 0x01
ValueB:
; Low byte, high byte.
0x00 0x00
+132
View File
@@ -0,0 +1,132 @@
; A Fibonacci number generating program that uses four bytes to store the value.
#Include Libraries/print.asm
#Program
start:
; Swap ValueB and ValueA.
; First, store ValueA on the stack.
SETD ValueA
LDA
PSHA
INCD
LDA
PSHA
INCD
LDA
PSHA
INCD
LDA
PSHA
; Next, store ValueB on the stack.
SETD ValueB
LDA
PSHA
INCD
LDA
PSHA
INCD
LDA
PSHA
INCD
LDA
PSHA
; Then pop ValueB into ValueA.
SETD ValueA
INCD INCD INCD
POPA
STA
DECD
POPA
STA
DECD
POPA
STA
DECD
POPA
STA
; Then pop ValueA into ValueB.
SETD ValueB
INCD INCD INCD
POPA
STA
DECD
POPA
STA
DECD
POPA
STA
DECD
POPA
STA
; Print ValueA.
SETD ValueA
INCD INCD INCD
LDA
CALL printByteHex
DECD
LDA
CALL printByteHex
DECD
LDA
CALL printByteHex
DECD
LDA
CALL printByteHex
CALL blankSpace
; Now add ValueA and ValueB, and store the result in ValueA.
; Add the lowest bytes of ValueA and ValueB.
SETD ValueB
LDB
SETD ValueA
LDA
ADD
; Store it in ValueA's lowest byte.
STQ
; Add the second lowest bytes of ValueA and ValueB.
SETD ValueB
INCD
LDB
SETD ValueA
INCD
LDA
ADD
; Store it in ValueA's second lowest byte.
STQ
; Add the second highest bytes of ValueA and ValueB.
SETD ValueB
INCD INCD
LDB
SETD ValueA
INCD INCD
LDA
ADD
; Store it in ValueA's third lowest byte.
STQ
; Add the highest bytes of ValueA and ValueB.
SETD ValueB
INCD INCD INCD
LDB
SETD ValueA
INCD INCD INCD
LDA
ADD
; If this addition overflows, we're done.
BRC end
; Otherwise, store the result in ValueA's highest byte.
STQ
; And branch back to the beginning of the loop.
BRI start
end:
CALL lineFeed
HALT
#Data
ValueA:
; Lowest byte ... Highest byte.
0x01 0x00 0x00 0x00
ValueB:
; Lowest byte ... Highest byte.
0x00 0x00 0x00 0x00
+33
View File
@@ -0,0 +1,33 @@
; A Fibonacci number generating program that uses only one byte to store the value.
#Include Libraries/print.asm
#Program
start:
; Load our initial values into A and B.
INIA 0x00
CALL printByteDecimal
CALL blankSpace
; Move the value into B.
PSHA
POPB
; Load the next starting value into A.
INIA 0x01
CALL printByteDecimal
CALL blankSpace
loop:
ADD ; Add the values together.
BRC end ; If the value overflows, we're done.
; Copy A into B
PSHA
POPB
; Copy Q into A
PSHQ
POPA
; Print A.
CALL printByteDecimal
CALL blankSpace
BRI loop ; Loop again.
end:
CALL lineFeed
HALT
+13 -8
View File
@@ -12,8 +12,13 @@ lineFeed:
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.
RET
; Expects A to contain the number of spaces to print.
blankSpaces:
INIB 0x20 ; Set B to the ASCII value for space.
OUTB 0x00 ; Print it.
BRA printDone ; If A is zero, we're done.
@@ -21,13 +26,13 @@ blankSpace:
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.
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:
+7 -11
View File
@@ -17,13 +17,11 @@ Instruction instruction_set[] = {
{0x01, "SUB"},
{0x02, "AND"},
{0x03, "OR"},
{0x04, "NOR"},
{0x05, "NAND"},
{0x06, "XOR"},
{0x07, "NOTA"},
{0x08, "NOTB"},
{0x09, "SHL"},
{0x0A, "SHR"},
{0x04, "XOR"},
{0x05, "NOTA"},
{0x06, "NOTB"},
{0x07, "SHL"},
{0x08, "SHR"},
// Branch Operations:
{0x10, "BRI"},
{0x11, "BRQ"},
@@ -31,10 +29,6 @@ Instruction instruction_set[] = {
{0x13, "BRB"},
{0x14, "BRC"},
{0x17, "CALL"},
{0x18, "CALLA"},
{0x19, "CALLB"},
{0x1A, "CALLQ"},
{0x1B, "CALLCF"},
{0x1F, "RET"},
// Register Operations:
{0x20, "RSTA"},
@@ -65,6 +59,8 @@ Instruction instruction_set[] = {
{0x45, "STA"},
{0x46, "STB"},
{0x47, "SETD"},
{0x48, "DPUP"},
{0x49, "DPDN"},
// Output Operations:
{0xD0, "OUTQ"},
{0xD1, "OUTA"},
+59 -56
View File
@@ -33,27 +33,27 @@ void genericBranch(CPURegisters *cpu){
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);
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) {
@@ -88,33 +88,25 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) {
cpu->Q = cpu->A|cpu->B;
break;
case 0x04:
// NAND - A nand B -> Q
cpu->Q = ~(cpu->A&cpu->B);
break;
case 0x05:
// NOR - A nor B -> Q
cpu->Q = ~(cpu->A|cpu->B);
break;
case 0x06:
// XOR - A xor B -> Q
cpu->Q = cpu->A^cpu->B;
break;
case 0x07:
case 0x05:
// NOTA - not A -> Q
cpu->Q = ~cpu->A;
break;
case 0x08:
case 0x06:
// NOTB - not B -> Q
cpu->Q = ~cpu->B;
break;
case 0x09:
case 0x07:
// 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 0x0A:
case 0x08:
// SHR - Shift AB right.
shiftRegister = ((uint16_t)cpu->A << 8) | cpu->B;
shiftRegister = (shiftRegister >> 1) | (shiftRegister << 15);
@@ -167,29 +159,29 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) {
// 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 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.
@@ -213,6 +205,7 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) {
cpu->ProgramCounter = cpu->ProgramCounter | (uint16_t)cpu->Data[cpu->StackPointer];
// Add 2 to the Program Counter to skip over the address when it returns.
cpu->ProgramCounter += 2;
break;
//
// 2x - Register Operations:
@@ -347,6 +340,16 @@ uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) {
Address |= (uint16_t)cpu->Program[cpu->ProgramCounter];
cpu-> DataPointer = Address;
break;
case 0x48:
// DPUP - Offset the Data Pointer up by the value of the next byte of Program Memory.
cpu->ProgramCounter++;
cpu->DataPointer += cpu->Program[cpu->ProgramCounter];
break;
case 0x49:
// DPDN - Offset the Data Pointer down by the value of the next byte of Program Memory.
cpu->ProgramCounter++;
cpu->DataPointer -= cpu->Program[cpu->ProgramCounter];
break;
//
// Dx - Output Operations:
//
+11 -15
View File
@@ -16,22 +16,20 @@ It has six registers:
## List of Instructions:
### Arithmetic and Logic Operations: 11 Instructions
### Arithmetic and Logic Operations: 9 Instructions
Hex Code | Mnemonic | Description
-- | -- | --
00 | ADD | Adds A, B, and the Carry Flag, the result is stored in Q.
01 | SUB | Subtracts B and the Carry Flag from A, the result is stored in Q.
02 | AND | Bitwise and of A and B, the result is stored in Q.
03 | OR | Bitwise or of A and B, the result is stored in Q.
04 | NOR | Bitwise nor of A and B, the result is stored in Q.
05 | NAND | Bitwise nand of A and B, the result is stored in Q.
06 | XOR | Bitwise xor of A and B, the result is stored in Q.
07 | NOTA | Bitwise inversion of A, the result is stored in Q.
08 | NOTB | Bitwise inversion of B, the result is stored in Q.
09 | SHL | A and B form a circular shift register. Rotate this register left.
0A | SHR | A and B form a circular shift register. Rotate this register right.
04 | XOR | Bitwise xor of A and B, the result is stored in Q.
05 | NOTA | Bitwise inversion of A, the result is stored in Q.
06 | NOTB | Bitwise inversion of B, the result is stored in Q.
07 | SHL | A and B form a circular shift register. Rotate this register left.
08 | SHR | A and B form a circular shift register. Rotate this register right.
### Branch Operations: 11 Instructions
### Branch and Subroutine Operations: 7 Instructions
Hex Code | Mnemonic | Description
-- | -- | --
10 | BRI | Branch Immediately. Loads the immediate next two bytes of Program Memory into the Program Counter, first the most significant byte, then the least.
@@ -40,10 +38,6 @@ Hex Code | Mnemonic | Description
13 | BRB | Branch on B. If B is zero, loads the immediate next two bytes of Program Memory into the Program Counter.
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.
@@ -73,17 +67,19 @@ Hex Code | Mnemonic | Description
35 | 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.
### Data Operations: 8 Instructions
### Data Operations: 10 Instructions
Hex Code | Mnemonic | Description
-- | -- | --
40 | INCD | Increments the Data Pointer.
41 | DECD | Decrements the Data Pointer.
42 | LDA | Loads the byte referenced from Data Memory by the Data Pointer into A.
43 | LDB | Loads the byte referenced from Data Memory by the Data Pointer into B.
44 | STQ | Stores Q into the byte referenced by the Data Pointer in Data Mmoery.
44 | STQ | Stores Q into the byte referenced by the Data Pointer in Data Memory.
45 | STA | Stores A into the byte referenced by the Data Pointer in Data Memory.
46 | STB | Stores B into the byte referenced by the Data Pointer in Data Memory.
47 | SETD | Loads the next two bytes of Program Memory into the Data Pointer.
48 | DPUP | Offset Data Pointer up by the value of the immediate next byte of Program Memory.
49 | DPDN | Offset Data Pointer down by the value of the immediate next byte of Program Memory.
### Output Operations: 3 Instructions