We have an ALU
We now have a port-mappable ALU. That means, the ALU is not connected directly to the CPU registers, but needs to be accessed via port I/O commands. This is indicated in the illustration below:
The ALU has the following 4 registers
- Operand A
- Operand B
- Instruction
- Result
These registers need to be accessed via 4 port addresses. In my implementation I am using the ports $fffc - $ffff.
On top of the registers, the ALU also provides 3 flags to the CPU:
- EQ/zero: set if a comparison yielded the result ‘equal’ or if the numerical result of an operation was 0.
- GT/carry: set if a comparison yielded the result ‘greater than’ or if an addition/subtraction over/underflowed or if a shift operation shifted a bit into this flag.
- LT: set if a comparison yielded the result ‘less than’
The ALU is capable of executing the following 14 instructions:
- add with carry
- subtract with carry
- shift left, MSB into carry
- shift right, LSB into carry
- bitwise rotate left
- bitwise rotate right
- bitwise or
- bitwise and
- bitwise nand
- bitwise xor
- bitwise compare
- increment by 1
- decrement by 1
Some of these instructions take 2 parameters, some only one.
Here is an excerpt of alu.inc
which defines constants and convenience macros for working with the ALU:
; -------------------
; -- ALU constants --
; -- for RRISC ALU --
; -------------------
const ALU_ADD = 0 ; addition with carry, sets carry (gt)
const ALU_SUB = 1 ; subtraction with carry, sets carry(gt)
const ALU_SHL = 2 ; shift left into carry, sets carry(gt)
const ALU_SHR = 3 ; shift right into carry, sets carry(gt)
const ALU_ROL = 4 ; rotate left
const ALU_ROR = 5 ; rotate right
const ALU_OR = 6 ; binary or
const ALU_AND = 7 ; binary and
const ALU_NAND = 8 ; binary not and
const ALU_XOR = 9 ; binary exclusive or
const ALU_XNOR = 10 ; binary exclusive not or
const ALU_CMP = 11 ; compare, sets carry (gt), sm, eq flags
const ALU_INC = 12 ; increments A by 1, sets equal (zero) flag on zero
const ALU_DEC = 13 ; decrements A by 1, sets equal (zero) flag on zero
; --
; -- port I/O mapping of ALU registers
; --
const ALU_PORT_A = $fffc
const ALU_PORT_B = $fffd
const ALU_PORT_INSTR = $fffe
const ALU_PORT_RESULT = $ffff
; --
; -- ALU convenience macros
; --
MACRODEF ADD_G
out G, ALU_PORT_A
ldg # $1
out G, ALU_PORT_B
ldg # ALU_ADD
out G, ALU_PORT_INSTR
in G, ALU_RESULT
ENDMACRO
So the following assembly snippet would add a value to register G, without having to deal with ALU port operations:
include alu.inc ; -- read the ALU macro definitions
org 0
ldg # $10 ; G = 10
macro ADD_G $20 ; instructs the ALU to add $20 to register G
; and to return the result in register G
; ...
^ toc