Skip to main content

Framed registers

Traditional CPU architectures integrate their registers directly into the microchip for the fastest possible access. By contrast, regular memory is located in separate microchips, which the CPU accesses using a memory bus.

Chombit introduces a novel (patent pending) concept called framed registers which are mapped to main memory and therefore also accessed via the bus. This means that the framed register has a memory address, a key difference from Chombit's conventional registers. Because bus access is slower, this is an inefficient design from an electronics perspective, however it facilitates learning by eliminating the assembly language bookkeeping that is typically required to move variables in and out of registers. Framed registers also simplify the effects of a compiler, making it easier to understand how source code corresponds to assembly code.

Despite these simplifications, from an educational perspective Chombit's architecture nonetheless preserves the character of a real CPU. Unlike an interpreted bytecode or intermediate language, each Chombit instruction represents an atomic operation that can be understood as a digital circuit. Each instruction has a predetermined clock cycle cost and operates directly on a traditional memory bank without any abstractions.

Debugger view

The Hybrix debugger displays the framed registers in the lower part of the CPU tab, with two view options:

  • LOCAL shows only the framed registers used in the currently active function (as determined by the debugger's symbol table, if available)
  • B:, P:, or I: shows all possible indexes for a given framed register size

"CPU" tab in the Hybrix debugger

Details

Consider the following CPU instruction, which uses two framed registers P:-7 and I:12:

C0_0000: E6 F9 03 56 78    LOAD P:-7, [I:12 + $5678]

Technical points:

  • The register name has a register size prefix B:, P:, or I: that indicates its storage size BYTE, PAIR, or INT.

  • The machine language opcode byte ($E6 in the above example) encodes the instruction kind (LOAD) as well as its register sizes. Here is how it appears in the LOAD documentation:

    OpcodeBytesCyclesForm
    $E654LOAD P:_, [I:_ + {S_PAIR}]
  • A register name has a register index suffix that is a signed decimal integer. For example, 7-7 in P:-7, or 1212 in I:12.

  • The operand byte encodes the register index using two's complement with a range from 64-64 ($C0) to 191191 ($BF).

  • For negative indexes, the operand byte is the two's complement of the index value. For example, 7-7 becomes $F9 in hexadecimal in the above example.

  • For positive indexes, the operand byte divides by the register size. For example, if our framed register is I:12, then 1212 divides by 44 bytes (for INT) to produce $03 in hexadecimal.

  • This means that positive indexes must be a multiple of the register size. For example, we can have P:66 (a multiple of 22), whereas P:67 and I:66 are impossible.

  • The notation {S_PAIR} simply means an literal (immediate) value that is a signed 16-bit integer, using decimal or hexadecimal syntax.

    The table below summarizes the ranges of possible register indexes:

    Storage sizeMinimumMaximum
    BYTEB:-64B:191
    PAIRP:-64P:382
    INTI:-64I:764
  • Modeling negative/positive indexes this way accommodates the Hybrix calling convention, where positive indexes represent local variables (numerous and easily reordered), whereas negative indexes represent function parameters (limited number and fixed order).

  • The register's storage address is computed by adding FP to the register index. For example, if FP is $0010_0009 then P:-7 refers to $0010_0002, and I:12 refers to $0010_0015 (because 9+12=219 + 12 = 21 which is $15 in hexadecimal).

  • Chombit always uses big-endian storage. For example, if FP is $0010_0009 and we store $ABCD in the framed register P:-7, then $AB is written at address $10_0002, and $CD is written at $10_0003.

  • Framed registers of different sizes can map to the same memory, in which case writing to one will alter the value of the other. For example, I:4 and P:6 will have two memory bytes in common. Typically this produces memory corruption bugs, however it could be leveraged for advanced optimizations.