Ahmes — Architecture Manual

1. Overview

Ahmes is a strict extension of Neander — same 8-bit word, same 256-byte memory, same single accumulator, same single addressing mode. What it adds is arithmetic breadth: subtraction, shift and rotate instructions, a carry/borrow/overflow flag trio, and a full set of conditional jumps keyed on those new flags.

Think of Ahmes as “Neander for people who have discovered that one flag is not enough to do multi-byte arithmetic.” Everything a programmer wrote for Neander still runs on Ahmes; the new instructions open up multi-precision arithmetic, bit-level manipulation, and signed comparisons.

Family position:

MachineNew capability over predecessor
NeanderBaseline: single accumulator, one addressing mode, N/Z flags
AhmesSUB, shifts, rotates; C, V, B flags; extended conditional jumps
RamsesMultiple registers (RA/RB/RX), four addressing modes, JSR
Cesar16-bit, eight registers, eight addressing modes, stack, memory-mapped I/O

Because Ahmes preserves Neander’s “one accumulator, one addressing mode” simplicity, a student’s mental model carries over intact — the only new lesson is “more flags, more arithmetic, more branches.”

Source of truth: fs/ArchSims.Core/Ahmes.fs, fs/ArchSims.Core/Memory.fs.

Note: the codebase does not ship an assembler for Ahmes (only Ramses and Cesar have one — this is intentional, matching Neander). The CLI runner also does not dispatch Ahmes (fs/ArchSims.CmdLine/Main.fs:78 raises Not implemented: Ahmes). Programs are exercised through the Ahmes F# module directly and through the test suite (fs/ArchSims.Core.Tests/AhmesTests.fs).

2. Programmer’s Model

2.1 Registers

Identical to Neander:

RegisterWidthRole
AC8 bitsAccumulator — the only data register
PC8 bitsProgram Counter — wraps 0xFF → 0x00

2.2 Instruction Register (IR)

Identical shape to Neander: OpCode byte and OperandAddress byte (0 if the instruction takes no operand).

2.3 Flags

Ahmes has five status flags plus Halted:

FlagMeaningInitial value
NegativeResult’s bit 7 is set (result > 0x7F)false
ZeroResult is 0true
CarryUnsigned arithmetic/shift carry out of bit 7false
OverflowSigned arithmetic overflowfalse
BorrowSUB produced a borrow (i.e., unsigned result went below 0)false
HaltedHLT just executed; cleared by next instructionfalse

Notes:

Suggested UI: show five flag lamps in a row labelled N Z V C B plus a distinct HLT indicator. Keep the order consistent across Ahmes/Ramses/Cesar even though each machine uses a different subset — the visual pattern is part of the teaching.

2.4 Memory

Unchanged from Neander: 256 bytes, flat, byte-addressable, no I/O mapping, no stack.

3. Instruction Set

Ahmes keeps all eleven Neander instructions with the same opcodes and adds new ones in the previously-unused opcode ranges.

3.1 Complete instruction table

Opcodes given as conventional UFRGS Ahmes encoding — high nibble is the primary selector, with sub-nibbles distinguishing jump-family variants and shift/rotate variants.

MnemonicOpcode (hex)BytesOperand?Flags touchedEffect
NOP0x001noDo nothing
STA a0x102yesMEM[a] ← AC
LDA a0x202yesN, ZAC ← MEM[a]
ADD a0x302yesN, Z, C, VAC ← AC + MEM[a]
OR a0x402yesN, ZAC ← AC OR MEM[a]
AND a0x502yesN, ZAC ← AC AND MEM[a]
NOT0x601noN, ZAC ← NOT AC
SUB a0x702yesN, Z, B, VAC ← AC − MEM[a]
JMP a0x802yesPC ← a
JN a0x902yesIf N=1, PC ← a
JP a0x942yesIf N=0 (positive), PC ← a
JV a0x982yesIf V=1, PC ← a
JNV a0x9C2yesIf V=0, PC ← a
JZ a0xA02yesIf Z=1, PC ← a
JNZ a0xA42yesIf Z=0, PC ← a
JC a0xB02yesIf C=1, PC ← a
JNC a0xB42yesIf C=0, PC ← a
JB a0xB82yesIf B=1, PC ← a
JNB a0xBC2yesIf B=0, PC ← a
SHR0xE01noN, Z, CLogical shift right: C ← AC[0]; AC ← AC >> 1; AC[7] ← 0
SHL0xE11noN, Z, CLogical shift left: C ← AC[7]; AC ← AC << 1; AC[0] ← 0
ROR0xE21noN, Z, CRotate right through carry: tmp ← C; C ← AC[0]; AC ← AC >> 1; AC[7] ← tmp
ROL0xE31noN, Z, CRotate left through carry: tmp ← C; C ← AC[7]; AC ← AC << 1; AC[0] ← tmp
HLT0xF01noHaltedStops the CPU

Decoding conventions locked in by Ahmes.fs:

3.2 Addressing

Single addressing mode — direct, identical to Neander. The operand byte is the address; no indirect, no immediate, no indexed.

To use an immediate value, store it as a data byte in memory and LDA it into AC. To compute a multi-byte value, build it up byte by byte using SUB/ADD with memory-resident operands.

4. Execution Cycle

Identical structure to Neander:

  1. Fetch — read opcode at PC, advance PC. If the opcode takes an operand, read and store it, advance PC again.
  2. Execute — dispatch on the opcode, perform the operation, update flags, set Halted iff the instruction was HLT.

For shifts and rotates, the implementation must:

For ADD and SUB:

The Halted flag follows the Neander convention: set at end of HLT execution, cleared by the next instruction (Neander.fs:132 for the pattern Ahmes should mirror). The debugger sees Halted=true after the step and stops the run loop; a UI should render it as a “just stopped” state, not a permanent one.

5. Programming Notes

6. Small Example Programs

Ahmes does not ship an assembler (intentional — same convention as Neander). Programs are shown as hand-assembled bytes, in the Neander style.

6.1 16-bit addition

Add the 16-bit number at [0xE0..0xE1] (big-endian: high byte at 0xE0, low at 0xE1) to the 16-bit number at [0xE2..0xE3]; store result at [0xE4..0xE5].

Address  Bytes        Source                  Comment
-------  -----------  ---------------------   ------------------------------------
  0      0x20 0xE1    LDA 0xE1                AC ← low(A)
  2      0x30 0xE3    ADD 0xE3                AC ← AC + low(B); C = low carry-out
  4      0x10 0xE5    STA 0xE5                store low byte of result
  6      0xB4 0x10    JNC 0x10                if no carry, skip the +1 on high byte
  8      0x20 0xE0    LDA 0xE0                AC ← high(A)
 10      0x30 0xF0    ADD 0xF0                AC ← AC + 1   (constant 1 at 0xF0)
 12      0x30 0xE2    ADD 0xE2                AC ← AC + high(B)
 14      0x80 0x14    JMP 0x14
 16      0x20 0xE0    LDA 0xE0                no-carry path: just add the highs
 18      0x30 0xE2    ADD 0xE2
 20      0x10 0xE4    STA 0xE4
 22      0xF0         HLT
 0xF0    0x01                                 constant 1

Non-trivial. The point is that it is possible on Ahmes and was not possible on Neander.

6.2 Sign-preserving divide by two

Address  Bytes        Source                  Comment
-------  -----------  ---------------------   -------------------------------
  0      0x20 0x10    LDA 0x10                AC ← value
  2      0x90 0x0A    JN  0x0A                if negative, go to negative path
  4      0xE0         SHR                     non-negative: shift once
  5      0x10 0x10    STA 0x10
  7      0xF0         HLT
  ...
 10      0xE0         SHR                     negative: shift...
 11      0x50 0x11    AND 0x11                ... (workaround to re-set bit 7
 13      0x40 0x12    OR  0x12                     using OR 0x80 after SHR)
 15      0x10 0x10    STA 0x10
 17      0xF0         HLT
 0x10    0xF0                                 example value = −16 (signed)
 0x11    0x7F                                 mask
 0x12    0x80                                 sign bit

Showing the student why the next machine (Cesar, with ASR) bothers having a dedicated arithmetic shift.

7. UI Design Suggestions

Ahmes looks almost exactly like Neander on screen — the UI should emphasize the differences rather than redecorate what is already there.

  1. Extend the flag row. Use the same lamp component as Neander but add C, V, B. Keep N Z V C B order for consistency with Ramses (N Z _ C) and Cesar (N Z V C). When the current instruction cannot touch a flag, fade its lamp; it prevents the false impression that e.g. OR updated V.
  2. Instruction-category badge. Since the instruction set grew to 24 opcodes, color-code them in the disassembly: Neander-original (teal), arithmetic extension (orange), shift/rotate (purple), extended jumps (yellow). Students recognize “this is new” at a glance.
  3. Flag-change indicator. On each step, briefly flash any flag lamp that changed state. In an ISA where SUB touches N/Z/V/B but not C, seeing exactly which lamps blink per instruction builds correct intuition faster than a cheatsheet.
  4. Carry/Borrow distinction. Because Ahmes distinguishes C from B, make the two lamps visually distinct (different icon, not just different position). A student who has internalized Ramses’s single C may otherwise expect one to feed the other.
  5. 16-bit scratchpad widget. Many Ahmes teaching exercises produce two-byte results. A small “joined cell” overlay in the memory grid — “these two bytes form a 16-bit value, here’s its decimal/signed interpretation” — makes multi-byte arithmetic tractable without forcing mental hex arithmetic.
  6. Shift/rotate animation. The shift/rotate instructions beg for an 8-cell bit strip widget showing AC’s bits, with an arrow from the outgoing end into C and (for rotates) from C back into the opposite end. This single animation conveys the difference between SHR/SHL/ROR/ROL more clearly than any prose.
  7. Conditional-jump predicate tooltip. For every Jxx, show the predicate on hover (JNC → if C=0, PC ← a). Given that there are ten conditional-jump mnemonics, this drastically reduces look-up friction.
  8. Disassembler. Following the pattern of DisassembleInstruction in Ramses.fs:221, an Ahmes disassembler should be straightforward. It needs to decode the four-way sub-opcode switch for conditional jumps and the four-way sub-opcode switch for shifts/rotates — the only non-trivial decoding in the ISA.
  9. Binary file format. If an Ahmes .mem loader is added later, follow the family convention: four-byte ASCII prefix ^C A H M (0x03 'A' 'H' 'M'), then the 256 memory bytes. Cf. RamsesCmdLine.fs:62 and CesarCmdLine.fs:74 — each simulator has a distinct prefix so loaders can refuse a mismatched file.
  10. Borrowing Neander UI. Ahmes shares Neander’s memory layout, cycle, and accumulator. A well-factored UI should reuse the memory grid, IR widget, register tiles, and debugger wiring from the Neander frontend, parameterizing only the flag row, the opcode decoder, and the disassembler. This also makes it easier to present “the same program running on Neander” side by side to show where Ahmes’s new flags and instructions help.