# Cardiograph Mark I — simulator for a paper 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: **N**egative, **Z**ero, **O**verflow, **C**arry - in machine language, each flag is given a number: - N = 3 Z = 2 O = 1 C = 0 - (bitwise, `0000 = NZOC`) ## Instruction set ### Operations ``` Hex Mnem. Name Operand type Effect --------------------------------------------- 00 END End (ignored) Halt CPU 01 NOP No op (ignored) None 50 STO Store literal # mem[lit#] = A 51 LDA Load literal # A = lit# 52 ADD Add literal # A = A + lit# 53 SUB Sub literal # A = A - lit# 54 HOP Hop literal # If A == lit#, skip next op (IP += 4) 55 JMP Jump literal # IP = lit# 56 FTG Flag toggle literal # Toggle flag, where flag number == lit# 57 FHP Flag hop literal # Skip next op if flag is set, where flag number == lit# 60 STO Store address mem[mem[addr]] = A 61 LDA Load address A = addr 62 ADD Add address A = A + mem[addr] 63 SUB Sub address A = A - mem[addr] 64 HOP Hop address If A == mem[addr], skip next instruction (IP += 4) 65 JMP Jump address IP = mem[addr] ``` ### Map + effects on flags, registers ``` hex bin group mode op mem flags IP ------------------------------------------------------- 00 0000 0000 0 -- END +2 01 0000 0001 0 -- NOP +2 50 0101 0000 1 direct STO w +2 51 0101 0001 1 direct LDA r NZ +2 52 0101 0010 1 direct ADD NZOC +2 53 0101 0011 1 direct SUB NZOC +2 54 0101 0100 1 direct HOP +2/+4 55 0101 0101 1 direct JMP arg 56 0101 0110 1 direct FTG NZOC +2 57 0101 0111 1 direct FHP NZOC +2/+4 60 0110 0000 1 indirect STO r,w +2 61 0110 0001 1 indirect LDA r,r NZ +2 62 0110 0010 1 indirect ADD r NZOC +2 63 0110 0011 1 indirect SUB r NZOC +2 64 0110 0100 1 indirect HOP r +2/+4 65 0110 0101 1 indirect JMP r arg 66 0110 0110 1 indirect FTG r NZOC +2 67 0110 0111 1 indirect FHP r NZOC +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-FF` - free ## 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 VIP 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