diff --git a/docs/architecture-specification.md b/docs/architecture-specification.md
new file mode 100644
index 0000000..89b2631
--- /dev/null
+++ b/docs/architecture-specification.md
@@ -0,0 +1,106 @@
+# [DRAFT] Specification for the _Cardiograph Architecture_
+
+## CPU
+
+### Registers
+
+There are four 8-bit registers:
+
+1. **A**, the accumulator (and the only general-purpose register)
+2. **IP**, the instruction pointer (aka program counter)
+3. **IOD**, the ID of the current I/O device
+3. **Status**
+
+#### Status register
+
+The *high byte* holds the state of the four Sense Switches. (TODO: is this easy enough to do in hardware?)
+
+The *low byte* holds four flags:
+**O**verflow, **N**egative, **Z**ero, and **C**arry.
+
+These are all addressed by number:*
+
+| S1 | S2 | S3 | S4 | | O | N | Z | C |
+|----|----|----|----|-|----|----|----|----|
+| 80 | 40 | 20 | 10 | | 08 | 04 | 02 | 01 |
+
+* (Because the core instruction set doesn't include bitwise operations)
+
+### Instruction set
+
+- Instructions are two bytes long:
+ one byte for the opcode, one for the operand
+
+TODO: revise this based on note dated 2023-09-24
+
+```GGMM IIII``` - **G**roup, **M**ode, **I**nstruction
+
+| lo ↓ / hi → | 0 (G0, M0) | 5 (G1, M1) | 6 (G1, M2) | 9 (G2, M1) | A (G2, M2) | F (G3, M3) |
+|-------------|------------|------------|------------|------------|------------|------------|
+| **0** | END | LDA # | LDA ind | DEV # | DEV ind | |
+| **1** | NOP | STO # | STO ind | INP # | INP ind | |
+| **2** | | ADD # | ADD ind | OUT # | OUT ind | |
+| **3** | | SUB # | SUB ind | FED | FED | |
+| **4** | | JMP # | JMP ind | | | |
+| **5** | | JEQ # | JEQ ind | | | |
+| **6** | | JFL # | JFL ind | | | |
+| **7** | | FTG # | FTG ind | | | |
+| | | | | | | |
+| **8** | | MUL # | MUL ind | | | RSL A |
+| **9** | | DIV # | DIV ind | | | RSR A |
+| **A** | | JLT # | JLT # | | | ASL A |
+| **B** | | JGT # | JGT # | | | ASR A |
+| **C** | | NOT # | NOT # | | | |
+| **D** | | AND # | AND # | | | |
+| **E** | | OR # | OR # | | | |
+| **F** | | XOR # | XOR # | | | |
+
+TODO: assess JMPs vs. HOPs
+
+- RSL/RSR: Ring Shift Left/Right
+- JLT/JGT: Jump Less/Greater Than
+- DEV: select IO device
+- FED: "feed" - line feed / end of card
+
+TODO: format/document better:
+
+1. core computational operations: low nibbles of 0x, 5x, 6x
+2. arithmetic extension (optional): MUL, DIV
+3. IO extension (optional): 9x, Ax
+4. bitwise arithmetic extension (optional): NOT, AND, OR, XOR and RSL, RSR, ASL, ASR
+5. control flow extension (optional): JLT, JGT
+
+- The mainframe system implements at least 1, 2, and 3
+- The microprocessor trainer implements 1
+- (see note dated 2023-09-24)
+
+
+### Connections (pinout)
+
+TBC
+
+| name | in/out? | description |
+|-----------|---------|---------------|
+| RST | in | *reset* |
+| VCC | in | *power* |
+| GND | in | *ground* |
+| CLK | in | *clock* |
+| A0 - A7 | out | *address bus* |
+| D0 - D7 | out | *data bus* |
+| ABE | out | *address bus enable*:
low when the CPU is using the address bus |
+| DBE | out | *data bus enable*:
low when the CPU is using the data bus |
+| WAIT | in | *wait* — when pulled low,
the current operation is completed
and then execution pauses |
+| /RD | out | TODO |
+| /WR | out | |
+| M/IO | out | |
+
+
+### Start-up
+
+TODO: see if this makes sense for the mainframe
+
+When starting up, the CPU executes a `JMP $FF`.
+
+Put differently: it starts executing instructions at the address contained in `$FF`.
+
+TODO: currently the simulator doesn't actually do this
\ No newline at end of file
diff --git a/docs/assembly-language.md b/docs/assembly-language.md
new file mode 100644
index 0000000..e9512e0
--- /dev/null
+++ b/docs/assembly-language.md
@@ -0,0 +1,31 @@
+# Assembly language
+
+## Syntax
+
+ 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
+
+- Prefix hexadecimal numbers with `$` (or `0x`)
+- Prefix binary numbers with `0b`
+- Whitespace is ignored
\ No newline at end of file
diff --git a/docs/mainframe-design.md b/docs/mainframe-design.md
new file mode 100644
index 0000000..96bfdd9
--- /dev/null
+++ b/docs/mainframe-design.md
@@ -0,0 +1,100 @@
+## Cardiograph Mark I (mainframe)
+
+The components of a Mark I are:
+
+- an CG 101 Central Processing Unit
+- an CG 102 Core Memory Unit
+- an CG 103 Print-Key-Punch
+- an CG 104 Matrix Display
+
+### Console
+
+TBC TBC TBC
+
+The console is equipped with:
+
+- Power switch
+- Load button
+- Run button
+- Run Single Step button
+- Halt button
+- Memory Read button
+- Memory Read Next button
+- Memory Write button
+- Memory Write Next button
+- 4 Sense Switches
+
+- 8 Accumulator lights
+- 8 Address lights
+- 8 Data lights
+- 8 Instruction Pointer lights (review IP size?)
+- 8 Status Register lights
+
+## IO programming
+
+Only one input or output device can be accessed at a time.
+
+### Reading data
+
+1. Use `DEV xx` to select input device _xx_
+2. Use `INP yy` to read one byte into memory at address _yy_
+
+TODO: find a way to allow the input device to refuse to provide input
+
+### Writing data
+
+1. Use `DEV xx` to select output device _xx_
+2. Use `OUT yy` to write one byte from memory at address _yy_
+3. Use `FED xx` to...
+ - card punch: load a new card
+ - printer: begin a new line (CR, LF)
+ - display: begin a new line
+
+### Punched card format
+
+FIXME:
+- ~~Cards are punched in EBCDIC~~
+- ~~EBCDIC data is translated into binary by the card reader/punch~~
+- Only columns 1-64 are used (for a maximum of 64 bytes of data per card)
+
+### Printer format
+
+- The printer format is the same as the card format
+- One line of printing is equivalent to one card
+- The printer can print up to 64 characters per line
+
+### Matrix display format
+
+- The display is a 5x5 grid of lights
+- Each light has 16 possible brightness levels (0 = off, 15 = maximum)
+- The display is written one line at a time
+- After the display is selected with `DEV`, writing begins on the top line
+- Writing wraps around and begins at the top again, if more than 5 lines are written
+
+### Device numbers
+
+1. card reader / typewriter
+2. card punch / line printer
+3. display
+
+### Print-Key-Punch configurations
+
+A dial allows you to select which input device to connect to the CPU:
+
+1. none
+2. card reader
+3. keyboard
+
+A similar dial selects the output device to connect:
+
+1. none
+2. card punch
+3. printer
+
+Thus, this all-in-one device allows the following configurations:
+
+| | printer | card punch | none |
+|-----------------|----------------------|------------------|--------------------------|
+| **keyboard** | ***teletypewriter*** | ***auto punch*** | ***keypunch (offline)*** |
+| **card reader** | (keys + print) | card duplicator | (card reader) |
+| **none** | line printer | (auto punch) | (scrap metal) |
\ No newline at end of file
diff --git a/docs/micro-design.md b/docs/micro-design.md
new file mode 100644
index 0000000..b34d8e2
--- /dev/null
+++ b/docs/micro-design.md
@@ -0,0 +1,40 @@
+# [DRAFT] Design for the _MicroCardiograph_ microprocessor trainer
+
+The MicroCardiograph uses memory-mapped IO.
+
+## Memory map
+
+| Address | Used for... |
+|----------|-----------------------------------------------|
+| 00 to 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 to FE | free |
+| FF | * ROM (unwriteable) pointer to initial IP |
+
+\* Not implemented yet
+
+
+## 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`
+
+The arrow keys are also mapped onto the hex keypad:
+
+` ` `5` ` ` = ` ` `↑` ` `
+`7` `8` `9` = `←` `↓` `→`
+
+### Keypad as monitor input
+
+TODO
\ No newline at end of file
diff --git a/readme.md b/readme.md
index 3da513b..c29479e 100644
--- a/readme.md
+++ b/readme.md
@@ -31,276 +31,9 @@ while the MicroCardiograph is designed to be used interactively.
_[Micro ElectroCardiograph (µECG)](micro/readme-micro.md)_ is a simulator for the Micro Cardiograph.
-## CPU
+## Documentation
-### Registers
-
-There are four 8-bit registers:
-
-1. **A**, the accumulator (and the only general-purpose register)
-2. **IP**, the instruction pointer (aka program counter)
-3. **IOD**, the ID of the current I/O device
-3. **Status**
-
-#### Status register
-
-The *high byte* holds the state of the four Sense Switches. (TODO: is this easy enough to do in hardware?)
-
-The *low byte* holds four flags:
-**O**verflow, **N**egative, **Z**ero, and **C**arry.
-
-These are all addressed by number:*
-
-| S1 | S2 | S3 | S4 | | O | N | Z | C |
-|----|----|----|----|-|----|----|----|----|
-| 80 | 40 | 20 | 10 | | 08 | 04 | 02 | 01 |
-
-* (Because the core instruction set doesn't include bitwise operations)
-
-### Instruction set
-
-- Instructions are two bytes long:
- one byte for the opcode, one for the operand
-
-TODO: revise this based on note dated 2023-09-24
-
-```GGMM IIII``` - **G**roup, **M**ode, **I**nstruction
-
-| lo ↓ / hi → | 0 (G0, M0) | 5 (G1, M1) | 6 (G1, M2) | 9 (G2, M1) | A (G2, M2) | F (G3, M3) |
-|-------------|------------|------------|------------|------------|------------|------------|
-| **0** | END | LDA # | LDA ind | DEV # | DEV ind | |
-| **1** | NOP | STO # | STO ind | INP # | INP ind | |
-| **2** | | ADD # | ADD ind | OUT # | OUT ind | |
-| **3** | | SUB # | SUB ind | FED | FED | |
-| **4** | | JMP # | JMP ind | | | |
-| **5** | | JEQ # | JEQ ind | | | |
-| **6** | | JFL # | JFL ind | | | |
-| **7** | | FTG # | FTG ind | | | |
-| | | | | | | |
-| **8** | | MUL # | MUL ind | | | RSL A |
-| **9** | | DIV # | DIV ind | | | RSR A |
-| **A** | | JLT # | JLT # | | | ASL A |
-| **B** | | JGT # | JGT # | | | ASR A |
-| **C** | | NOT # | NOT # | | | |
-| **D** | | AND # | AND # | | | |
-| **E** | | OR # | OR # | | | |
-| **F** | | XOR # | XOR # | | | |
-
-TODO: assess JMPs vs. HOPs
-
-- RSL/RSR: Ring Shift Left/Right
-- JLT/JGT: Jump Less/Greater Than
-- DEV: select IO device
-- FED: "feed" - line feed / end of card
-
-TODO: format/document better:
-
-1. core computational operations: low nibbles of 0x, 5x, 6x
-2. arithmetic extension (optional): MUL, DIV
-3. IO extension (optional): 9x, Ax
-4. bitwise arithmetic extension (optional): NOT, AND, OR, XOR and RSL, RSR, ASL, ASR
-5. control flow extension (optional): JLT, JGT
-
-- The mainframe system implements at least 1, 2, and 3
-- The microprocessor trainer implements 1
-- (see note dated 2023-09-24)
-
-
-### Connections (pinout)
-
-TBC
-
-| name | in/out? | description |
-|-----------|---------|---------------|
-| RST | in | *reset* |
-| VCC | in | *power* |
-| GND | in | *ground* |
-| CLK | in | *clock* |
-| A0 - A7 | out | *address bus* |
-| D0 - D7 | out | *data bus* |
-| ABE | out | *address bus enable*:
low when the CPU is using the address bus |
-| DBE | out | *data bus enable*:
low when the CPU is using the data bus |
-| WAIT | in | *wait* — when pulled low,
the current operation is completed
and then execution pauses |
-| /RD | out | TODO |
-| /WR | out | |
-| M/IO | out | |
-
-
-### Start-up
-
-TODO: see if this makes sense for the mainframe
-
-When starting up, the CPU executes a `JMP $FF`.
-
-Put differently: it starts executing instructions at the address contained in `$FF`.
-
-TODO: currently the simulator doesn't actually do this
-
-
-## Cardiograph Mark I (mainframe)
-
-The components of a Mark I are:
-
-- an CG 101 Central Processing Unit
-- an CG 102 Core Memory Unit
-- an CG 103 Print-Key-Punch
-- an CG 104 Matrix Display
-
-### Console
-
-TBC TBC TBC
-
-The console is equipped with:
-
-- Power switch
-- Load button
-- Run button
-- Run Single Step button
-- Halt button
-- Memory Read button
-- Memory Read Next button
-- Memory Write button
-- Memory Write Next button
-- 4 Sense Switches
-
-- 8 Accumulator lights
-- 8 Address lights
-- 8 Data lights
-- 8 Instruction Pointer lights (review IP size?)
-- 8 Status Register lights
-
-## IO programming
-
-Only one input or output device can be accessed at a time.
-
-### Reading data
-
-1. Use `DEV xx` to select input device _xx_
-2. Use `INP yy` to read one byte into memory at address _yy_
-
-TODO: find a way to allow the input device to refuse to provide input
-
-### Writing data
-
-1. Use `DEV xx` to select output device _xx_
-2. Use `OUT yy` to write one byte from memory at address _yy_
-3. Use `FED xx` to...
- - card punch: load a new card
- - printer: begin a new line (CR, LF)
- - display: begin a new line
-
-### Punched card format
-
-FIXME:
-- ~~Cards are punched in EBCDIC~~
-- ~~EBCDIC data is translated into binary by the card reader/punch~~
-- Only columns 1-64 are used (for a maximum of 64 bytes of data per card)
-
-### Printer format
-
-- The printer format is the same as the card format
-- One line of printing is equivalent to one card
-- The printer can print up to 64 characters per line
-
-### Matrix display format
-
-- The display is a 5x5 grid of lights
-- Each light has 16 possible brightness levels (0 = off, 15 = maximum)
-- The display is written one line at a time
-- After the display is selected with `DEV`, writing begins on the top line
-- Writing wraps around and begins at the top again, if more than 5 lines are written
-
-### Device numbers
-
-1. card reader / typewriter
-2. card punch / line printer
-3. display
-
-### Print-Key-Punch configurations
-
-A dial allows you to select which input device to connect to the CPU:
-
-1. none
-2. card reader
-3. keyboard
-
-A similar dial selects the output device to connect:
-
-1. none
-2. card punch
-3. printer
-
-Thus, this all-in-one device allows the following configurations:
-
-| | printer | card punch | none |
-|-----------------|----------------------|------------------|--------------------------|
-| **keyboard** | ***teletypewriter*** | ***auto punch*** | ***keypunch (offline)*** |
-| **card reader** | (keys + print) | card duplicator | (card reader) |
-| **none** | line printer | (auto punch) | (scrap metal) |
-
-## MicroCardiograph (microprocessor trainer)
-
-The MicroCardiograph uses memory-mapped IO.
-
-### Memory map
-
-| Address | Used for... |
-|----------|-----------------------------------------------|
-| 00 to 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 to FE | free |
-| FF | * ROM (unwriteable) pointer to initial IP |
-
-\* Not implemented yet
-
-
-### 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`
-
-The arrow keys are also mapped onto the hex keypad:
-
-` ` `5` ` ` = ` ` `↑` ` `
-`7` `8` `9` = `←` `↓` `→`
-
-## 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
-
-- Prefix hexadecimal numbers with `$` (or `0x`)
-- Prefix binary numbers with `0b`
-- Whitespace is ignored
\ No newline at end of file
+- [Specification for the Cardiograph Architecture](docs/architecture-specification.md)
+- [Design for the mainframe computer](docs/mainframe-design.md)
+- [Design for the micro computer](docs/micro-design.md)
+- [Assembly Language](docs/assembly-language.md)
\ No newline at end of file