diff --git a/.gitignore b/.gitignore index f8f7461..979d941 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ Tests/build/ +Object/ +Assembler +SplitBit +CLAUDE.md diff --git a/Programs/16bitFibonacci.bin b/Programs/16bitFibonacci.bin new file mode 100644 index 0000000..6282bac Binary files /dev/null and b/Programs/16bitFibonacci.bin differ diff --git a/Programs/16bitSegmentedSieve.bin b/Programs/16bitSegmentedSieve.bin new file mode 100644 index 0000000..ace554c Binary files /dev/null and b/Programs/16bitSegmentedSieve.bin differ diff --git a/Programs/16x16Life.bin b/Programs/16x16Life.bin new file mode 100644 index 0000000..9f4f7c6 Binary files /dev/null and b/Programs/16x16Life.bin differ diff --git a/Programs/32bitFibonacci.bin b/Programs/32bitFibonacci.bin new file mode 100644 index 0000000..00ea154 Binary files /dev/null and b/Programs/32bitFibonacci.bin differ diff --git a/Programs/8bitFibonacci.bin b/Programs/8bitFibonacci.bin new file mode 100644 index 0000000..77dc8b5 Binary files /dev/null and b/Programs/8bitFibonacci.bin differ diff --git a/Programs/Libraries/int32.asm b/Programs/Libraries/int32.asm new file mode 100644 index 0000000..7be20b4 --- /dev/null +++ b/Programs/Libraries/int32.asm @@ -0,0 +1,79 @@ +#Program +; These routines untested and written entirely by Claude Code! +; int32 subroutines. + +; An int32 value is a 32-bit unsigned integer, represented by four bytes, +; in the order most-significant to least-significant. + +; The basic operations take two sequential int32 values as operands from Data Memory. +; They assume that the Data Pointer is set to the high byte of Operand A. +; Operands are stored in an eight-byte array in the following order: +; Operand A, byte 3 (most significant) +; Operand A, byte 2 +; Operand A, byte 1 +; Operand A, byte 0 (least significant) +; Operand B, byte 3 (most significant) +; Operand B, byte 2 +; Operand B, byte 1 +; Operand B, byte 0 (least significant) +; All operations overwrite Operand A. This causes the Data Pointer to point to the result after returning. + +int32add: + ; Add two int32 values. + ; If the addition results in a carry, the carry flag will be set. + CCF ; Clear the Carry Flag. + DPUP 0d07 ; Move to Operand B's byte 0 (least significant). + LDB ; Load it to B. + DPDN 0d04 ; Move to Operand A's byte 0. + LDA ; Load it to A. + ADD ; Add them together. + STQ ; Store the result. + DPUP 0d03 ; Move to Operand B's byte 1. + LDB ; Load it to B. + DPDN 0d04 ; Move to Operand A's byte 1. + LDA ; Load it to A. + ADD ; Add with carry from byte 0. + STQ ; Store the result. + DPUP 0d03 ; Move to Operand B's byte 2. + LDB ; Load it to B. + DPDN 0d04 ; Move to Operand A's byte 2. + LDA ; Load it to A. + ADD ; Add with carry from byte 1. + STQ ; Store the result. + DPUP 0d03 ; Move to Operand B's byte 3 (most significant). + LDB ; Load it to B. + DPDN 0d04 ; Move to Operand A's byte 3. + LDA ; Load it to A. + ADD ; Add with carry from byte 2. + STQ ; Store the result. + RET ; Return to the caller. + +int32sub: + ; Subtract Operand B from Operand A. + ; If the subtraction results in a borrow, the carry flag will be set. + CCF ; Clear the Carry Flag. + DPUP 0d07 ; Move to Operand B's byte 0 (least significant). + LDB ; Load it to B. + DPDN 0d04 ; Move to Operand A's byte 0. + LDA ; Load it to A. + SUB ; Subtract B from A. + STQ ; Store the result. + DPUP 0d03 ; Move to Operand B's byte 1. + LDB ; Load it to B. + DPDN 0d04 ; Move to Operand A's byte 1. + LDA ; Load it to A. + SUB ; Subtract with borrow from byte 0. + STQ ; Store the result. + DPUP 0d03 ; Move to Operand B's byte 2. + LDB ; Load it to B. + DPDN 0d04 ; Move to Operand A's byte 2. + LDA ; Load it to A. + SUB ; Subtract with borrow from byte 1. + STQ ; Store the result. + DPUP 0d03 ; Move to Operand B's byte 3 (most significant). + LDB ; Load it to B. + DPDN 0d04 ; Move to Operand A's byte 3. + LDA ; Load it to A. + SUB ; Subtract with borrow from byte 2. + STQ ; Store the result. + RET ; Return to the caller. diff --git a/Programs/gameOfLife/16x16Life.asm b/Programs/gameOfLife/16x16Life.asm new file mode 100644 index 0000000..ac3406e --- /dev/null +++ b/Programs/gameOfLife/16x16Life.asm @@ -0,0 +1,326 @@ +; Conway's Game of Life for SplitBit. +; +; The visible field is 16x16, surrounded by a permanently dead one-cell border. +; Each of the 18x18 allocated cells is stored as two adjacent bytes: +; current state, next state +; This makes double buffering possible with SplitBit's single Data Pointer. +; +; The initial pattern is a glider. ANSI terminal control codes redraw the field +; in place. Press Ctrl-C to stop the emulator. + +#Include Libraries/print.asm + +#Program + +start: + CALL seedGlider + SETD ClearScreen + CALL printString + +generationLoop: + CALL renderBoard + CALL evolveBoard + CALL commitBoard + CALL frameDelay + BRI generationLoop + +seedGlider: + ; Coordinates in the padded field: + ; .#. + ; ..# + ; ### + SETD Board + DPUP 0d42 + INIA 0x01 + STA + SETD Board + DPUP 0d80 + STA + SETD Board + DPUP 0d112 + STA + SETD Board + DPUP 0d114 + STA + SETD Board + DPUP 0d116 + STA + RET + +renderBoard: + SETD CursorHome + CALL printString + SETD Board + DPUP 0d38 + INIA 0d16 + SETD RowCount + STA + SETD Board + DPUP 0d38 + +renderRow: + PSHD + INIA 0d16 + SETD ColCount + STA + POPD + +renderCell: + LDA + BRA renderDead + INIB 0x23 + OUTB 0x00 + BRI renderCellDone +renderDead: + INIB 0x20 + OUTB 0x00 +renderCellDone: + DPUP 0d02 + PSHD + SETD ColCount + LDA + DECA + STA + BRA renderRowDone + POPD + BRI renderCell + +renderRowDone: + POPD + CALL lineFeed + DPUP 0d04 + PSHD + SETD RowCount + LDA + DECA + STA + BRA renderDone + POPD + BRI renderRow + +renderDone: + POPD + RET + +evolveBoard: + SETD Board + DPUP 0d38 + INIA 0d16 + SETD RowCount + STA + SETD Board + DPUP 0d38 + +evolveRow: + PSHD + INIA 0d16 + SETD ColCount + STA + POPD + +evolveCellLoop: + CALL evolveCell + DPUP 0d02 + PSHD + SETD ColCount + LDA + DECA + STA + BRA evolveRowDone + POPD + BRI evolveCellLoop + +evolveRowDone: + POPD + DPUP 0d04 + PSHD + SETD RowCount + LDA + DECA + STA + BRA evolveDone + POPD + BRI evolveRow + +evolveDone: + POPD + RET + +; DP points to the current-state byte of a visible cell. +evolveCell: + CALL countNeighbors + PSHQ + LDA + POPB + + ; Three neighbors always produces a live cell. + INIA 0d03 + CCF + SUB + BRQ makeAlive + + ; Two neighbors preserve the current state. + INIA 0d02 + CCF + SUB + BRQ preserveCell + +makeDead: + RSTA + INCD + STA + DECD + RET + +preserveCell: + LDA + BRA makeDead + +makeAlive: + INIA 0x01 + INCD + STA + DECD + RET + +; Return the sum of the eight neighboring current-state bytes in Q. +; With interleaved cells and an 18-cell row, the relative offsets are: +; -38, -36, -34, -2, +2, +34, +36, +38. +countNeighbors: + RSTA + DPDN 0d38 + + LDB + CCF + ADD + PSHQ + POPA + DPUP 0d02 + + LDB + CCF + ADD + PSHQ + POPA + DPUP 0d02 + + LDB + CCF + ADD + PSHQ + POPA + DPUP 0d32 + + LDB + CCF + ADD + PSHQ + POPA + DPUP 0d04 + + LDB + CCF + ADD + PSHQ + POPA + DPUP 0d32 + + LDB + CCF + ADD + PSHQ + POPA + DPUP 0d02 + + LDB + CCF + ADD + PSHQ + POPA + DPUP 0d02 + + LDB + CCF + ADD + RET + +; Copy every next-state byte to its adjacent current-state byte. +; The border's next bytes remain zero, so it stays permanently dead. +commitBoard: + SETD Board + INIA 0d18 + SETD RowCount + STA + SETD Board + +commitRow: + PSHD + INIA 0d18 + SETD ColCount + STA + POPD + +commitCell: + INCD + LDA + DECD + STA + DPUP 0d02 + PSHD + SETD ColCount + LDA + DECA + STA + BRA commitRowDone + POPD + BRI commitCell + +commitRowDone: + POPD + PSHD + SETD RowCount + LDA + DECA + STA + BRA commitDone + POPD + BRI commitRow + +commitDone: + POPD + RET + +; Approximately 0.2 seconds at the emulator's nominal 1 MHz rate. +frameDelay: + INIA 0xFF +delayOuter: + INIB 0xFF +delayInner: + DECB + BRB delayInnerDone + BRI delayInner +delayInnerDone: + DECA + BRA delayDone + BRI delayOuter +delayDone: + RET + +#Data + +RowCount: + 0x00 +ColCount: + 0x00 + +ClearScreen: + 0x1B + "[2J" +CursorHome: + 0x1B + "[H" + +; The emulator zero-fills the remainder of Data Memory. Board names the first +; byte of a 648-byte logical allocation (18 * 18 * 2). +Board: + 0x00 diff --git a/Programs/primeSieve/16bitSegmentedSieve.asm b/Programs/primeSieve/16bitSegmentedSieve.asm new file mode 100644 index 0000000..0d616a5 --- /dev/null +++ b/Programs/primeSieve/16bitSegmentedSieve.asm @@ -0,0 +1,236 @@ +; A segmented Sieve of Eratosthenes for the complete 16-bit address range. +; +; The sieve uses a 256-byte sliding window. Each entry in PrimeStates contains: +; prime, high byte of next multiple, low byte of next multiple +; +; Once a prime becomes active at p*p, adding an 8-bit prime to offsets in a +; 256-byte window must wrap exactly once before the next window. The wrapped +; low byte becomes that prime's starting offset in the following window. +; +; Output is hexadecimal (0002 through FFFD), separated by spaces. + +#Include Libraries/print.asm + +#Program + +start: + RSTA + SETD Page + STA + +nextPage: + ; Clear all 256 flags. A wrapping to zero terminates the loop. + SETD Segment + RSTA + RSTB +clearSegment: + STB + INCD + INCA + BRA segmentCleared + BRI clearSegment + +segmentCleared: + ; Zero and one are not prime. + SETD Page + LDA + BRA excludeZeroAndOne + BRI markSegment +excludeZeroAndOne: + SETD Segment + INIA 0x01 + STA + INCD + STA + +markSegment: + ; Process the 54 primes not greater than sqrt(0xFFFF). + SETD PrimeStates + INIA 0d54 +primeLoop: + CALL processPrime + DPUP 0d03 + DECA + BRA scanSegment + BRI primeLoop + +scanSegment: + ; A is the low byte of the candidate and wraps after 0xFF. + SETD Segment + RSTA +scanLoop: + LDB + BRB emitPrime +scanNext: + INCD + INCA + BRA advancePage + BRI scanLoop + +emitPrime: + CALL printCandidateHex + BRI scanNext + +advancePage: + SETD Page + LDA + INCA + STA + BRA finished + BRI nextPage + +finished: + CALL lineFeed + HALT + +; DP points at a PrimeStates entry on entry and is preserved by CALL. +processPrime: + PSHD + INCD + LDA + SETD Page + LDB + XOR + BRQ primeIsActive + POPD + RET + +primeIsActive: + ; Recover and retain the state-entry pointer for the final update. + POPD + PSHD + + ; Load the prime into B and its current offset into A. + LDA + PSHA + DPUP 0d02 + LDA + POPB + + ; Form Segment + offset. Segment is page-aligned in Data Memory. + SETD Segment + PSHB + PSHD + POPB + CCF + ADD + PSHQ + POPD + POPB + +markPrimeLoop: + INIA 0x01 + STA + + ; Add the prime to the low byte of DP using the stack as a 16-bit + ; address adapter. Carry means that the next multiple is in the next page. + PSHD + POPA + CCF + ADD + PSHQ + POPD + BRC primeFinished + BRI markPrimeLoop + +primeFinished: + ; Q is the wrapped offset for the next page. + POPD + INCD + LDA + INCA + STA + INCD + STQ + RET + +; A contains the candidate's low byte. Page contains its high byte. +printCandidateHex: + PSHA + SETD Page + LDA + CALL printByteHex + POPA + CALL printByteHex + CALL blankSpace + RET + +#Data + +; print.asm deliberately pads its data to one page, so this begins at 0x0100. +Segment: + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + +Page: + 0x00 + +PrimeStates: + 0x02 0x00 0x04 + 0x03 0x00 0x09 + 0x05 0x00 0x19 + 0x07 0x00 0x31 + 0x0B 0x00 0x79 + 0x0D 0x00 0xA9 + 0x11 0x01 0x21 + 0x13 0x01 0x69 + 0x17 0x02 0x11 + 0x1D 0x03 0x49 + 0x1F 0x03 0xC1 + 0x25 0x05 0x59 + 0x29 0x06 0x91 + 0x2B 0x07 0x39 + 0x2F 0x08 0xA1 + 0x35 0x0A 0xF9 + 0x3B 0x0D 0x99 + 0x3D 0x0E 0x89 + 0x43 0x11 0x89 + 0x47 0x13 0xB1 + 0x49 0x14 0xD1 + 0x4F 0x18 0x61 + 0x53 0x1A 0xE9 + 0x59 0x1E 0xF1 + 0x61 0x24 0xC1 + 0x65 0x27 0xD9 + 0x67 0x29 0x71 + 0x6B 0x2C 0xB9 + 0x6D 0x2E 0x69 + 0x71 0x31 0xE1 + 0x7F 0x3F 0x01 + 0x83 0x43 0x09 + 0x89 0x49 0x51 + 0x8B 0x4B 0x79 + 0x95 0x56 0xB9 + 0x97 0x59 0x11 + 0x9D 0x60 0x49 + 0xA3 0x67 0xC9 + 0xA7 0x6C 0xF1 + 0xAD 0x74 0xE9 + 0xB3 0x7D 0x29 + 0xB5 0x7F 0xF9 + 0xBF 0x8E 0x81 + 0xC1 0x91 0x81 + 0xC5 0x97 0x99 + 0xC7 0x9A 0xB1 + 0xD3 0xAD 0xE9 + 0xDF 0xC2 0x41 + 0xE3 0xC9 0x49 + 0xE5 0xCC 0xD9 + 0xE9 0xD4 0x11 + 0xEF 0xDF 0x21 + 0xF1 0xE2 0xE1 + 0xFB 0xF6 0x19 diff --git a/Programs/primeSieve/8bitSieve.bin b/Programs/primeSieve/8bitSieve.bin new file mode 100644 index 0000000..e36da67 Binary files /dev/null and b/Programs/primeSieve/8bitSieve.bin differ diff --git a/Programs/printHello.bin b/Programs/printHello.bin new file mode 100644 index 0000000..f8d640a Binary files /dev/null and b/Programs/printHello.bin differ diff --git a/Programs/replCalculator.asm b/Programs/replCalculator.asm new file mode 100644 index 0000000..4e88c00 --- /dev/null +++ b/Programs/replCalculator.asm @@ -0,0 +1,410 @@ +; Interactive hexadecimal calculator for SplitBit. +; +; Enter expressions as: +; HH operator HH +; Whitespace is optional. Supported operators are + - * & | and ^. +; Arithmetic wraps to eight bits. Enter Q to quit. + +#Include Libraries/print.asm + +#Program + +start: + SETD Welcome + CALL printString + +repl: + SETD Prompt + CALL printString + + CALL readNonSpace + PSHQ + POPA + + ; Q, q, or end-of-file exits. + INIB 0xFF + XOR + BRQ quit + INIB 0x51 + XOR + BRQ quit + INIB 0x71 + XOR + BRQ quit + + CALL readHexByteFirst + SETD LeftOperand + STQ + CALL parseIsInvalid + BRQ inputError + + CALL readNonSpace + PSHQ + POPA + SETD Operator + STA + + CALL readNonSpace + PSHQ + POPA + CALL readHexByteFirst + SETD RightOperand + STQ + CALL parseIsInvalid + BRQ inputError + + CALL discardLine + CALL evaluate + SETD Result + STQ + CALL operatorIsInvalid + BRQ operatorError + + SETD Result + LDA + CALL printByteHex + CALL lineFeed + BRI repl + +inputError: + CALL discardLine + SETD BadInput + CALL printString + BRI repl + +operatorError: + SETD BadOperator + CALL printString + BRI repl + +quit: + SETD Goodbye + CALL printString + HALT + +; Read a character other than space, tab, CR, or LF. Return it in Q. +readNonSpace: +readNonSpaceLoop: + INA 0x00 + INIB 0x20 + XOR + BRQ readNonSpaceLoop + INIB 0x09 + XOR + BRQ readNonSpaceLoop + INIB 0x0A + XOR + BRQ readNonSpaceLoop + INIB 0x0D + XOR + BRQ readNonSpaceLoop + INIB 0x00 + CCF + ADD + RET + +; A contains the first hexadecimal digit. Read the second and return the byte +; in Q. Q is 0xFF on malformed input. +readHexByteFirst: + CALL clearParseStatus + CALL hexNibble + PSHQ + POPA + INIB 0xFF + XOR + BRQ invalidByte + INIB 0x00 + SHL + SHL + SHL + SHL + PSHA + + CALL readNonSpace + PSHQ + POPA + CALL hexNibble + PSHQ + POPA + INIB 0xFF + XOR + BRQ invalidLowNibble + POPB + OR + RET + +invalidLowNibble: + POPB +invalidByte: + PSHD + SETD ParseStatus + INIA 0x01 + STA + POPD + INIA 0xFF + INIB 0x00 + CCF + ADD + RET + +; Convert the ASCII hexadecimal digit in A to a value in Q. +; Uppercase and lowercase letters are accepted. Q=0xFF means invalid. +hexNibble: + PSHA + + ; Try 0 through 9. + INIB 0x30 + CCF + SUB + BRC tryUpperHex + PSHQ + POPA + INIB 0d10 + CCF + SUB + BRC decimalNibble + +tryUpperHex: + POPA + PSHA + INIB 0x41 + CCF + SUB + BRC tryLowerHex + PSHQ + POPA + INIB 0d06 + CCF + SUB + BRC upperNibble + +tryLowerHex: + POPA + PSHA + INIB 0x61 + CCF + SUB + BRC badNibble + PSHQ + POPA + INIB 0d06 + CCF + SUB + BRC lowerNibble + +badNibble: + POPA + INIA 0xFF + INIB 0x00 + CCF + ADD + RET + +decimalNibble: + POPB + INIB 0x00 + CCF + ADD + RET + +upperNibble: + POPB + INIB 0d10 + CCF + ADD + RET + +lowerNibble: + POPB + INIB 0d10 + CCF + ADD + RET + +; Return Q=0 if Q was 0xFF, otherwise return a nonzero value. +resultIsInvalid: + PSHQ + POPA + INIB 0xFF + XOR + RET + +clearParseStatus: + PSHD + SETD ParseStatus + RSTA + STA + POPD + RET + +parseIsInvalid: + PSHD + SETD ParseStatus + LDA + INIB 0x01 + XOR + POPD + RET + +operatorIsInvalid: + PSHD + SETD OperatorStatus + LDA + INIB 0x01 + XOR + POPD + RET + +; Evaluate the stored expression and return its result in Q. +evaluate: + PSHD + SETD OperatorStatus + RSTA + STA + POPD + SETD Operator + LDA + + INIB 0x2B + XOR + BRQ evaluateAdd + INIB 0x2D + XOR + BRQ evaluateSubtract + INIB 0x2A + XOR + BRQ evaluateMultiply + INIB 0x26 + XOR + BRQ evaluateAnd + INIB 0x7C + XOR + BRQ evaluateOr + INIB 0x5E + XOR + BRQ evaluateXor + + SETD OperatorStatus + INIA 0x01 + STA + RSTA + INIB 0x00 + CCF + ADD + RET + +evaluateAdd: + SETD LeftOperand + LDA + INCD + LDB + CCF + ADD + RET + +evaluateSubtract: + SETD LeftOperand + LDA + INCD + LDB + CCF + SUB + RET + +evaluateAnd: + SETD LeftOperand + LDA + INCD + LDB + AND + RET + +evaluateOr: + SETD LeftOperand + LDA + INCD + LDB + OR + RET + +evaluateXor: + SETD LeftOperand + LDA + INCD + LDB + XOR + RET + +evaluateMultiply: + RSTA + SETD Product + STA + +multiplyLoop: + SETD RightOperand + LDA + BRA multiplyDone + DECA + STA + + SETD Product + LDA + SETD LeftOperand + LDB + CCF + ADD + SETD Product + STQ + BRI multiplyLoop + +multiplyDone: + SETD Product + LDA + INIB 0x00 + CCF + ADD + RET + +; Consume the rest of the current input line. +discardLine: +discardLoop: + INA 0x00 + INIB 0x0A + XOR + BRQ discardDone + INIB 0xFF + XOR + BRQ discardDone + BRI discardLoop +discardDone: + RET + +#Data + +LeftOperand: + 0x00 +RightOperand: + 0x00 +Operator: + 0x00 +Product: + 0x00 +Result: + 0x00 +ParseStatus: + 0x00 +OperatorStatus: + 0x00 + +Welcome: + "SplitBit calculator (+ - * & | ^), Q quits." +Prompt: + 0x0A + "> " +BadInput: + "Invalid hexadecimal input." +BadOperator: + "Unknown operator." +Goodbye: + "Goodbye!" + 0x0A diff --git a/Programs/replCalculator.bin b/Programs/replCalculator.bin new file mode 100644 index 0000000..4d89b0f Binary files /dev/null and b/Programs/replCalculator.bin differ diff --git a/Programs/testPrograms/dataPointerTest.asm b/Programs/testPrograms/dataPointerTest.asm new file mode 100644 index 0000000..006b977 --- /dev/null +++ b/Programs/testPrograms/dataPointerTest.asm @@ -0,0 +1,54 @@ +; Tests the Data Pointer calling convention. +; +; Every Data Pointer is aimed at a different marker byte, then a subroutine +; repoints all four of them at Z and returns. +; +; DP0 through DP2 are preserved across a CALL, so they should come back still +; aimed at their own markers. DP3 is volatile, which is what lets a subroutine +; hand an address back to its caller, so it should come back aimed at Z. +; +; Correct output is ABCZ. + +#Program + +start: + SETD.0 MarkA + SETD.1 MarkB + SETD.2 MarkC + SETD.3 MarkD + CALL clobber + + ; Read a byte through each pointer in turn and print it. + LDA.0 + OUTA 0x00 + LDA.1 + OUTA 0x00 + LDA.2 + OUTA 0x00 + LDA.3 + OUTA 0x00 + + INIA 0x0A + OUTA 0x00 + HALT + +clobber: + ; Aim every pointer somewhere else. Only the change to DP3 should outlive the RET. + SETD.0 MarkZ + SETD.1 MarkZ + SETD.2 MarkZ + SETD.3 MarkZ + RET + +#Data + +MarkA: +"A" +MarkB: +"B" +MarkC: +"C" +MarkD: +"D" +MarkZ: +"Z" diff --git a/Programs/testPrograms/twoPointerCopy.asm b/Programs/testPrograms/twoPointerCopy.asm new file mode 100644 index 0000000..cd7772f --- /dev/null +++ b/Programs/testPrograms/twoPointerCopy.asm @@ -0,0 +1,46 @@ +; Copies a string from one place in Data Memory to another, reading through DP0 +; and writing through DP1 at the same time. +; +; On a machine with one Data Pointer this loop needs a PSHD and a POPD on every +; pass to swap the pointer between the two regions. With two pointers it is just +; a load and a store. +; +; It also checks that a bare mnemonic still means DP0. + +#Program + +start: + SETD.0 Source ; DP0 walks the source. + SETD.1 Dest ; DP1 walks the destination. + +copy: + LDA.0 ; Read a byte through DP0. + BRA copyDone ; A zero byte is the end of the string. + STA.1 ; Write it through DP1. + INCD.0 + INCD.1 + BRI copy + +copyDone: + SETD Dest ; No selector, so this is DP0. +print: + LDA + BRA end + OUTA 0x00 + INCD + BRI print + +end: + INIA 0x0A + OUTA 0x00 + HALT + +#Data + +Source: +"Two pointers, no stack shenanigans." + +Dest: +0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00