# 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: **O**verflow, **N**egative, **Z**ero, **C**arry - 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