A1

From Esolang
Jump to navigation Jump to search

A1 is a saucy esoteric RISC architecture discovered by User:Orby in April of 2017.

Purpose

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.

Registers

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.

Instructions

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

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

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.

Examples

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:

External resources

See also