Home‎ > ‎retrocomputing‎ > ‎

Fable, a FPGA Able

The Able processor fits nicely in an FPGA, occupying about 300 slices of a Spartan3 for the processor, in addition to ROMs/RAMs and devices. An implementation in VHDL is here,.

The processor implementation is based on a close reading of the manual, but may not be completely accurate.

On-chip devices and memory are not completely asynchronous, but share the processor global clock. They may acknowledge on any rising edge, including the request.  Interrupt support is optional, and does not attempt to reproduce the Able.

A small assembler here generates code as a VHDL array. Instructions use assignment operators instead of MOV, and hexadecimal instead of octal. 


Registers are r0 through rF. Reading a register can post-increment it. As a special case, rC is post-decremented when storing to memory, making it useful as a downward-growing stack pointer. Note that both increment and decrement are applied post-read, so they are not exact inverses.

Memory is always addressed with a register - the only addressing mode is register-indirect, with an optional post-increment/decrement. Adding an address offset requires an accumulator operation.

Source Operand

The lower half of an instruction word specifies the source operand.

bits assembler use
0------- d0 ... d3F devices
10------ n immediate value -20 ... 1F
1100---- r0 ... rF register
1101---- r0++ ... rF++ register post-increment
1110---- [r0] ... [rF] memory load from register address
1111---- [r0++] ... [rF++] memory load from post-incremented address

16-bit immediate values are loaded from the instruction stream using a PC post-increment source, [rF++] (or [rE++] in interrupt code), which loads the word following an instruction and skips over it.

Destination Operand

The upper half of an instruction word specifies the destination operand. Destinations include ALU accumulator operations and conditional jumps.

bits assembler use
0------- d0 ... d3F = devices
100----- r0 ... r3 op= set accumulator and conditions
1010---- r0 ... r3 op? test accumulator and set conditions
1011---- r0 ... r3 op= set accumulator and conditions
1100---- r0 ... rF = register
1101--- jump op= conditional jumps
1110---- [r0] ... [rF] = memory store to register address
1111---- [r0++] ... [rF++] = memory store to post-incremented address
11111100 [rC--] = memory store to post-decremented address


 ALU operations combine the source with one of four accumulator registers, r0 through r3. The result is written back to the accumulator

bits assembler use
100000-- r0 ... r3 := acc = source
100001-- r0 ... r3 &= acc &= source
100010-- r0 ... r3 += acc += source
100011-- r0 ... r3 -= acc -= source
100100-- r0 ... r3 ^= acc ^= source
100101-- r0 ... r3 <= acc = ROL(acc,1)
100110-- r0 ... r3 |= acc |= source
100111-- r0 ... r3 ~= acc = ~source
101100-- r0 ... r3 %= acc = ROL(acc,8)|source
101101-- r0 ... r3 >= acc = SHR(acc,1)+source
101110-- r0 ... r3 +c= acc += source+c
101111-- r0 ... r3 -c= acc -= source+c

ALU destinations set three condition codes. Add/subtract-with-carry and conditional jumps read these codes.

M Minus negative result
C Carry carry out
Z Zero zero result

Test destinations perform the ALU operationand update condition codes, but do not update the accumulator register. Tests are limited to a subset of ALU operations.
bits assembler use
100000-- r0 ... r3 :? source
100001-- r0 ... r3 &? acc & source
100010-- r0 ... r3 +? acc + source
100011-- r0 ... r3 -? acc - source


Special destinations provide absolute and relative conditional jumps operating on the current PC (rF or rE).

11010--- jump := jump absolute
11011--- jump += jump relative

1101-0-- j jump if true
1101-1-- jn jump if false

1101--00 always
1101--01 z if zero
1101--10 c if carry
1101--11 m if minus

Jumps are implemented as load/add to the current PC, without updating conditions and updating the register only if the condition is met.

Additionally, assigning to the PC causes an unconditional absolute jump. Adding an immediate to the PC causes a short relative jump.


The Able used a bidirectional tristated bus. This isn't practical in an FPGA, so Fable uses a daisy-chained bus similar to Wishbone

The Able reserved bits in a processor status word (PSW) for each kind of device capable of interrupts. Here interrupt support is optional, provided through an interrupt control word (ICW) device which merely schedules interrupts. Interrupt status amd configuration is left to each device.  Interrupts are edge-triggered, so no device operation is required to clear the interrupt.

E --1 if interrupts are enabled
I -1- if the processor is currently interrupted
Q 1-- if an interrupt is pending

Clearing I exits interrupt handling. Setting Q causes a software interrupt.

The processor maintains two sets of condition codes, one for each interrupt state, so an interrupt handler need not save and restore them. As noted above, each interrupt state has its own PC, rE or rF. Any other registers must be managed explicitly.

Subpages (4): assembler fables tests vhdl