CARDIAC
This is the home of Strange Query CARDIAC, the world's first full CARDIAC IDE, featuring:
- Full support for the 1968 language as defined in the manual and implemented in my earlier compiler (and many others)
- An interactive editor that understands CARDIAC opcodes and auto-completes the six-column listing style used by the manual and most listings on the internet today
- A visual debugger that shows you the state of the machine at all times
- A stupid fast emulator that executes CARDIAC at near-native speed. This emulator also compiles to web assembly, allowing you to develop and debug programs right from your browser.
But wait, there's more
This debugger is the only one in the world to offer integration with a real ANSI terminal emulator (via xterm). Now, for the first time ever, your CARDIAC programs can output text and colors and interact with users. Hello, World actually outputs the text "Hello, World!". The terminal supports both an easy mode and a more faithful mode if you want to try your hand at ANSI special effects.
Support for multi-tape overlays
CARDIAC memory is so constrained that it's hard to write programs that are interesting at all.
This runtime helps you get around this with overlay support. Supply the runtime with a set of numbered tapes, and when you
issue an HRS instruction with a nonzero address in the operand, the machine will look to see if it has a tape with that ID. If it does,
it will load that program over the current program and start executing it.
This is supported both in the runtime and in the debugger. To see it work, pick sortem from the debugger's examples:
tape 1 reads 35 numbers and sorts them, then HRS 02 brings in tape 2, which prints them with their mean, minimum, maximum
and median. Its source is in cardiac-code/sortem.
New and exciting sample programs
There are sample programs in the StrangeQuery/cardiac-code repo. Not a lot there now but you'll find every program from the classic 1968 manual as well as new ones I developed (with Claude's help, of course). This includes what I believe is the world's first CARDIAC quine.
You can run it right now. For free. Just click here.
A quick CARDIAC rundown
The CARDIAC famously has a tiny amount of memory, only 100 addresses that each hold a single signed 3 digit value ranging from -999 to 999. Note that these are not binary values. The CARDIAC is not a binary computer. All operations are done against natural base 10 numbers and digits. Instructions and data share the cells. There's exactly one register that holds four digits so that it can support overflow. A program counter says which cell the next instruction comes from. There is exactly one branch instruction, addition and subtraction, and that's about it. I/O is a deck of cards, one number to a card, and output is cards punched with numbers. As mentioned above, this implementation allows input and output to go back and forth to an ANSI terminal.
Despite these humble resources, the CARDIAC is capable of a surprising amount of computation, something which I will demonstrate thoroughly over the next month or so.
Instructions
Brian L. Stuart addresses these in more depth on his CARDIAC site. I will have more to say about this later, but for now here is a quick rundown. The debugger offers an animated mode that will illustrate the effects of these instructions on the machine as they operate when stepping over your program in debug mode.
Every CARDIAC instruction is a three-digit word. The first digit of an instruction is the operation, the other two name an address, so 152 is
CLA 52 (opcode 1; the second two digits name the address).
| Op | Name | Does |
|---|---|---|
0 | INP | read a card into the cell. If you're hooked up in ANSI terminal mode, this will read from the terminal. |
1 | CLA | clear the accumulator and copy the value from the named address into it. |
2 | ADD | add the value from the named address to the accumulator |
3 | TAC | if the accumulator is negative, jump to the address. Notice that this is the only branch instruction. This has a huge effect on what coding patterns run efficiently on the CARDIAC. |
4 | SFT | shift the accumulator: left by the second digit, then right by the third |
5 | OUT | write the value stored at the named address to the output tape. In the debugger, this value will show up in the debug console. If you launch the terminal, what you'll see depends on the terminal mode. If you're running it in easy mode, a value 65 will render on the terminal as the digits "65". If you're not running in easy mode, it's on you to translate text that you want to display to the ASCII character set. If you want to display the value "4" on the terminal you need to output 52. If you want to emit the letter "A" you need to emit 65. |
6 | STO | copy the current value from the accumulator to the named cell |
7 | SUB | subtract the value in the named cell from the accumulator. |
8 | JMP | jump to the address. CARDIAC is a little weird in the way it handles jumps. It doesn't really support subroutines per se, but every time you do a jump, a second jump instruction is written to the special location 99. This way, when a subroutine finishes, it can just jump to location 99, which will trampoline back to the instruction after the jump that called it. |
9 | HRS | halt and reset the program counter to indicate the named address. In this implementation, HRS looks to see if there's another tape with the ID of the named address, and if it finds one, it will load it and continue running it. If it doesn't find one, it will stop execution the same way all previous CARDIAC implementations have done. |
You really have to hand it to the designer of this instruction set: there is a sly symmetry to these opcodes. Many of them come in pairs. Read and write, add and subtract, etc. Pairs of instructions like this are typically exactly 5 opcodes apart. So for example ADD has the value 2 and SUB has the value 7. INP is 0, OUT is 5. Load to the accumulator (CLA) is 1, store from the accumulator (STO) is 6. Branch (TAC) is 3, JMP is 8.
Art from adversity
The tiny amount of memory available, and the fact that code and data have to fight for that memory makes programming for the CARDIAC a fun challenge. Things that are typically nearly free on other platforms can be expensive on the CARDIAC, particularly branching. Other kinds of things are relatively cheap because this is not a base 2 computer. And some things are completely missing. No ANDs, ORs or XORs here. Self-modifying code is common, and other tricks like using a value stored in a cell as both an instruction and a constant value help save precious cells.
An example
Countdown prints 5, 4, 3, 2, 1, 0. Each line is a cell: address, contents, label, instruction, comment.
10 530 loop OUT n print n 11 130 CLA n 12 731 SUB one n - 1 13 316 TAC done below zero: stop 14 630 STO n 15 810 JMP loop 16 900 done HRS 00 30 005 n DATA 5 31 001 one DATA 1
This program is bundled in the debugger. Press Step (F11) to go one instruction at a time.
Go ahead, give it a try! Click this link and Hello, World will be loaded and waiting in a familiar browser experience. Start stepping over the code and watch the debug console and terminal to see it go.
