Go to file
n loewen 072d2ccdb5 (notes) 2023-08-20 - Create note with list of short-term goals 2023-08-29 08:26:17 -04:00
issues cpu - WIP - Start re-configuring CLI interface, in order to allow passing clock speed as a CLI argument (while also keeping a defualt value in machine.config.js) 2023-08-28 09:10:21 -04:00
notes (notes) 2023-08-20 - Create note with list of short-term goals 2023-08-29 08:26:17 -04:00
src Add `argparser` as a submodule 2023-08-28 09:18:51 -04:00
test-programs (tests) WIP - Create a rough, partial sketch for an implementation of Conway's Game of Life 2023-08-28 15:31:57 -04:00
.gitignore Add gitignore 2023-08-28 23:05:39 -04:00
.gitmodules Add `argparser` as a submodule 2023-08-28 09:18:51 -04:00
readme.md config / cpu - Add feature: Map the arrow keys to hex keypad, in addition to the larger Qwerty set 2023-08-26 16:44:30 -04:00

readme.md

Cardiograph Mark I — simulator for an imaginary computer

Dependencies

  • Node.js
    • readline-sync

Run

Assemble

Hex output:
./run-assembler run source_code.asm

Binary output:
./run-assembler runbin source_code.asm

Verbose debugging output (hex):
./run-assembler debug source_code.asm

Assemble and run

With animated display of screen memory:
./run-cpu run source_code.asm

With verbose debugging output:
./run-cpu debug source_code.asm

With single stepping + pretty-printed display:
./run-cpu step source_code.asm

With single stepping + verbose debugging output:
./run-cpu stepdebug source_code.asm

Registers and Flags

  • A - accumulator
  • IP - instruction pointer (aka program counter)
  • FLAGS - flags: Overflow, Negative, Zero, Carry
    • in machine language, each flag is given a number:
      • O = 3
        N = 2
        Z = 1
        C = 0
    • (bitwise, 0000 = ONZC)

Instruction set

Operations

Hex   Mnem.  Operand      Effect

00    END    (ignored)    Halt CPU
01    STO    literal #    mem[lit#] = A
02    STO    address      mem[mem[addr]] = A
03    LDA    literal #    A = lit#
04    LDA    address      A = addr
05    ADD    literal #    A = A + lit#
06    ADD    address      A = A + mem[addr]
07    SUB    literal #    A = A - lit#
08    SUB    address      A = A - mem[addr]
09    HOP    literal #    If A == lit#, skip next op (IP += 4)
0A    HOP    address      If A == mem[addr], skip next instruction (IP += 4)
0B    JMP    literal #    IP = lit#
0C    JMP    address      IP = mem[addr]
0D    FTG    literal #    Toggle flag, where flag number == lit#
0E    FHP    literal #    Skip next op if flag is set, where flag number == lit#
0F    NOP    (ignored)    None
  • Instructions are two bytes long: one byte for the opcode, one for the operand

Effects on memory, flags, registers

op   mem  flags  IP

END              +2
NOP              +2

STO  w           +2
LDA  r    NZ     +2
ADD       ONZC   +2
SUB       ONZC   +2
HOP              +2/+4
JMP              arg
FTG       ONZC   +2
FHP       ONZC   +2/+4

STO  r,w         +2
LDA  r,r  NZ     +2
ADD  r    ONZC   +2
SUB  r    ONZC   +2
HOP  r           +2/+4
JMP  r           arg
FTG  r    ONZC   +2
FHP  r    ONZC   +2/+4

CPU start-up

When starting up, the CPU executes a JMP $FF.

Put differently: it starts executing instructions at the address contained in $FF.

Cardiograph memory map

  • 00-19 - display (5x5)
  • 1A - pointer to display memory
  • 1B - keypad: value of latest key pressed
  • 1C - reserved for future use (bank switching flag)
  • 1D - initial IP
  • 1D-FE - free
  • FF - ROM (unwriteable) pointer to initial IP (not yet implemented)

Peripherals

Keypad

The value of the latest keypress on a hex keypad is stored at $1B.

The keypad uses the same layout as the COSMAC VIP (and CHIP-8). The CPU simulator maps those keys onto a Qwerty set:

1 2 3 C    1 2 3 4
4 5 6 D    Q W E R
7 8 9 E    A S D F
A 0 B F    Z X C V

hex pad    simulator

The arrow keys are also mapped onto the hex keypad:

   5           ↑
 7 8 9       ← ↓ →

hex pad    simulator

Assembly language

ADD $01         ; comments follow a `;`

ADD $FF         ; this is direct addressing
ADD ($CC)       ; this is indirect addressing

END             ; END and NOP don't require operands
                ; (the assembler will fill in a default value of 0)

@subroutine     ; create a label
    ADD $01     ; (it must be on the line before the code it names)
    ADD $02

JMP @subroutine ; use a label as operand
                ; the label will be replaced with
                ; the address of the label

#foo $FF        ; define a constant
                ; (must be defined before it is referenced)

ADD #foo        ; use a constant as an operand

LDA *           ; `*` is a special label referencing the memory address
                ; where the current line will be stored after assembly
  • Hexadecimal numbers are preceded by a $
  • Whitespace is ignored