A1 is a saucy esoteric RISC architecture discovered by User:Orby in April of 2017.
The purpose of A1 is to provide an minimal architecture that has reasonable code density. The instruction set should be small, but programs should not be unnecessarily large. A1 is a RAM machine with eight registers and three commands: Push (PSH), Pop (POP) and Subtract If Zero Flag Is Set (SUBZF). This lifts it out of the OISC category, but it remains simple enough to be called minimal IMHO.
The bit width of each register is implementation defined, and thus so is the amount of RAM that can be addressed. It is recommended to refer to n-bit A1 as A1xn (e.g. A1x32, A1x64, etc.)
The most commonly used A1 opcodes are only one or two bytes in size. This is in contrast to the majority of OISCs. ByteByteJump for example, requires 12 bytes per instruction to address the same amount of RAM. A1 does not require self-modifying code to achieve TC, so in theory optimizing VMs are possible. This also makes it easier to write A1 assembly code.
A1 has eight n-bit registers. The A, B, C, X, and Y registers are general purpose. The IP register is the instruction pointer which is equal to the address of the next instruction to execute. The SP register is the stack pointer. The ZF register is the zero flag and is used by the SUBZF instruction. All registers may be accessed directly or indirectly.
An rvalue is a 4-bit value that encodes a direct or indirect register reference. They map in the following way
$0 A $8 [A] $1 B $9 [B] $2 C $A [C] $3 X $B [X] $4 Y $C [Y] $5 ZF $D [ZF] $6 SP $E [SP] $7 IP $F [IP]
where [x] denotes a the value at the memory address pointed to by x.
A1 supports five opcodes which each execute in a single clock cycle (x represents an rvalue):
Hex Assembly Name 00 imm8 PSH imm8 Push (8-bit immediate value) 10 imm-n PSH imm-n Push (n-bit immediate value) 2x PSH x Push (Register) 3x POP x Pop 40 SUBZF Subtract if ZF is set
All other opcodes produce undefined results.
Push an immediate value or a register onto the stack. That is,
SP := SP - n/8 [SP] := x
The 8 bit immediate version sign extends the immediate value. ZF is unaffected.
Pop an immediate value directly to a register or to the address a register references. That is,
x := [SP] SP := SP + n/8
ZF is unaffected.
Subtract If Zero
Pop two values off the stack. Subtract the first value from the second value. If ZF is non-zero, then push the difference onto the stack and set ZF to the difference. If ZF is zero, then push the second value onto the stack and leave ZF alone.
All examples assume ZF is non-zero and n = 32. Here is an infinite loop in 5 bytes
push ip push 1 subzf pop ip
Set A := A + B in 7 bytes
psh a psh 0 psh b subzf subzf pop a
Function calls are simple. Here is an example in 12 bytes
psh Resume psh MyFunc pop ip Resume: ; More code here MyFunc: ; Do my function pop ip
Here is an example which counts backward from 100 (19 bytes)
psh 100 loop: ; Do stuff here ; Decrement psh 1 subzf ; If zf != 0 then jump to loop, otherwise jump to end psh end-loop psh end subzf pop ip end:
- A1 Implementation a Verilog implementation of A1