Boris' Simple Showcase Architecture, Nova (new revision)
Find a file
2026-07-29 12:43:48 +03:00
schematic Bring D-register inverter closer to reality 2026-07-29 12:43:48 +03:00
src Initial copy from development repo. 2026-07-28 12:16:42 +03:00
.gitmodules Initial copy from development repo. 2026-07-28 12:16:42 +03:00
Design Requirements.md Initial copy from development repo. 2026-07-28 12:16:42 +03:00
overview.png Initial copy from development repo. 2026-07-28 12:16:42 +03:00
overview_print.png Initial copy from development repo. 2026-07-28 12:16:42 +03:00
README.md Remove old irrelevant notes 2026-07-28 12:22:03 +03:00

Boris' Simple Showcase Architecture, Nova

A (relatively) simple but still capable computer architecture, based around the ability to build it out of 7400-series ICs.

Inspired by, among others:

Logisim schematic screenshot

Repository structure

.
├── Design Requirements.md
├── overview.png             // Logisim schematic export as image
├── overview_print.png       // Printable version of ^
├── README.md                // This document
├── schematic
│   ├── kicad                // KiCAD schematics; TBD
│   └── logisim              // Logisim-evolution schematics
└── src
    ├── basm                 // Assembler; Git submodule
    └── examples             // Example programs
        └── bin              // Assembled example programs

The Logisim schematic can be loaded with Logisim-evolution (standard Logisim and Digital not tested). In it, a TTY-output peripheral is configured at address 0xFF. ASCII characters written to that address show up on the TTY.

Assembled programs (like those in src/examples/bin) can be loaded into memory, and execution can be started from address 0x00 (which should be the default reset value of the program counter).

If you encounter any bugs/peculiarities, have other comments and/or improvement ideas, feel free to open issues or otherwise get in touch.

Goals

Main goal is to be an understandable, highly visual representation of what a computer is actually doing when a program is running.

Detailed design goals are listed in (the design requirements document)[Design Requirements.md]

Core specs:

  • Data word can be an integer multiple of 8 bits, minimum factor is 1.
  • Word addressing only
  • Memory-memory architecture (no explicit register access)
  • Two-operand architecture, destination overwritten
  • Execution starts at 0x00
  • Instruction operands are either pointers to data, or pointers to pointers to data
  • Program Counter is 1 word wide
  • Separate PC for interrupt mode, on an expansion card
  • No flag/status register
  • Instruction format:
Word 0 Word 1 Word 2
Opcode Source Destination

Opcode is always the lowest 8 bits of the opcode word, the rest are filled with 0 to retain compatibility with the narrowest ISA. Instructions are always 3 words wide.

Architecture:

Two registers: Source and Destination (S and D). Both can contain either a memory address (*) or a pointer to a memory address (**). Whether S and D are * or ** is determined by the opcode (see below). When referring to source and destination operands, it is meant the values which are loaded once S and D are fully dereferenced and which are fed into the ALU. When referring to source and destination addresses, it is meant the address which is stored in the register after load, if the instruction contains a pointer to a memory address, or after first dereference, if the instruction contains a pointer to pointer (So the value after (**) -> (*), but not after (*) -> in-memory value). See examples below for details.

Program counter register (PC), which is a word wide and can be indirectly written to / read from. When doing branches or jumps, the source address (dereferenced one or zero times, depending on the instruction) is loaded into the PC.

No flag registers - conditional branches are done based on checks of the currently loaded destination operand.

Interrupts are handled only once the current instruction finishes executing (no interrupting during instruction execution).

Instruction set:

Opcode semantics:

If bit in opcode is set (bit==1):

  • Bit 0: Depends on instruction type.
  • Bit 1: Depends on instruction type.
  • Bit 2: Negate D operand.
  • Bit 3: Zero-out D operand.
  • Bit 4: Don't store result; 1 => result is not written
  • Bit 5: D address is a ** if 0, * if 1.
  • Bit 6: S address is a ** if 0, * if 1.
  • Bit 7: Instruction type; 0 => Data manipulation, 1 => Control flow

All 256 bit permutations are legal, although not all of them make sense. The ones that do are listed below and have a dedicated mnemonic in the assembly language.

Full ISA table:

Opcode Mnemonic Name Meaning Notes
0SD1---- NOP No operation Advance PC Alt encoding: 0SD0111-
0SD00000 ADD Add D = S + D
0SD00001 ADI Add with increment D = S + D + 1
0SD0001- AND Bitwise AND D = S & D
0SD00100 SBD Subtract with decrement D = S - D - 1
0SD00101 SUB Subtract D = S - D
0SD0011- ANN AND with negation of D D = S & ~D
0SD01000 MOV Move source to destination D = S Alt encoding: 0SD01101, 0SD0111-
0SD01001 INC Increment source D = S + 1
0SD0101- NUL Zero (set D to 0) D = 0 Source op is irrelevant
0SD01100 DEC Decrement source D = S - 1
1SD1--00 JMP Jump unconditionally PC = S Destination op is unaffected
1SD10001 JLZ Jump if less than 0 if (D < 0) then PC = S
1SD10010 JEZ Jump if equal to 0 if (D == 0) then PC = S
1SD10011 JGZ Jump if greater than 0 if (D < 0) then PC = S
1SD0--00 CAL Call subprocedure D = PC; PC = S

In the table, - stands for "don't care", meaning it does not matter whether the bits are set to 0 or 1. However, it is advised to set the "don't care" bits to 0 for future-proofing. SD stand for "source and data type indicators", and when set to 0, the values of the S and D (respectively) parts of the instruction are dereferenced twice for data manipulation instructions. For control flow instructions (JMP, CAL, etc.) the source is dereferenced only once! Therefore, the target address (which is the address in the source field) is directly a part of the instruction, and does not need to be stored elsewhere in memory. (You do have the option of a single dereference though, if you need to for example return to an address given by a stack pointer.) The destination field behaves the same for both kinds of instructions. The value at the address pointed at by the D-part is used as an operand, and then the same memory cell is overwritten with the result. See below for examples.

Some instructions have alternate encodings, which lead to the same results. Please do not use them, use the canonical encodings. This is especially true for control flow instructions, where it is easy to construct instructions which always or never branch. For them, alternate encodings are NOT listed in the table above.

A conditional jump which stores the PC is admissible by design, however, it has very limited use since the PC overwrites the conditional operand which is compared with 0. Therefore, such a jump is not listed as an officially supported instruction.

Instruction types

Data manipulation instructions

Operate on values in memory. Combining them with bits 2 and 3 from the opcode you can get additional operations.

Table of ALU operations:

Opcode bits 1:0 Opeartion
00 D = D + S
01 D = D + S + 1
1- D = D & S

Control flow instructions

Load the program counter based on a condition or unconditionally. May also store the program counter.

D operand is checked when doing conditions according to the following table:

Opcode bits 1:0 Meaning
00 Jump (branch always taken)
01 Branch if D < 0
10 Branch if D == 0
11 Branch if D > 0

If condition is fulfulled, the S operand is stored in the PC. If the Write bit is set, the PC is stored in the D address. This can be used to do jump-and-link-style instructions, but is more or less only useful with unconditional branches.

Pseudoinstructions

None yet, may be added to the assembler in the future.

ABI:

Stack

  • Fixed memory address as a "stack pointer"
  • Stack pointer points to a non-free space

Calling convention

To be specified.

Interrupts

The processor has no built-in interrupt handling. However, it exposes a "program count enable" (PCE) pin, which has to be kept high during normal operation. If the PCE pin is pulled low, the program counter does not increment and does not output its value on the address bus during instruction fetching. However, the PC still does get overwritten by jump/call instructions and is output on the data bus. This is done to allow program counter saving and overwrite from inside the (possibly short) interrupt handler, enabling the handler to jump to a more complex handling code once returned to normal operating mode (PCE high).

The state of the PCE pin shall only change outside of an instruction fetch, i.e. once the whole instruction has been fetched.

Notes & open questions

  • Implementation document
  • Interrupts
  • Does the stack grow upwards or downwards?
  • Calling convention

Reasonings:

  • No internal interrupt handling as it would require too much extra circuitry & wouldn't be easily daisy-chainable

  • Interrupt takes more than 1 cycle b/c of software stack + don't know how to otherwise handle the currently loaded instruction

  • Memory-memory architecture as otherwise every 2nd instruction is a load/store

  • Two-operand machine allows us to do conditional jumps w/o a flags register

  • No flags register / processor state (aside from decoding state machine ofc) as it overcomplicates interrupt handling. One option would be to expose the flags register in memory?

  • TODO: Document what happens inside implementation diagram

  • INT through second PC

  • DROPPED: Hardware Stack Pointer at a fixed MMIO address

  • Input through keypad + shift registers

  • TODO: What signals need to be daisy-chained?

  • TODO: Assembler

  • TODO: KiCAD breadboard prototyping