MIC-1
The mic1 package provides tools for working with the MIC-1 processor architecture that appears in Andrew S. Tanenbaum’s textbook Structured Computer Organization.
1 MIC-1 Description
The MIC-1 is a CPU with 16 general purpose 16-bit registers. Registers 5, 6, 7, 8, and 9 have default values 0000000000000000, 0000000000000001, 1111111111111111, 0000111111111111, and 0000000011111111 respectively.
It runs a single 256-instruction microprogram embedded in a control store ROM. Its ALU supports addition, bitwise AND, and bitwise negation. The ALU outputs flags for whether its result was negative or zero. The ALU is connected to a 1-bit shifter that can shift left, right, or not at all.
Its memory interface is two flags (one for reading, one for writing) as well as two 16-bit registers for interfacing with memory (the MAR–Memory Address Register–and MBR–Memory Buffer Register.) The top 4 bits of the MAR is ignored, so the MIC-1 has a 12-bit address space. Memory access is delayed by one cycle, during which the appropriate flag must be asserted. If both flags are asserted, then the external controller halts the machine.
The ALU’s A side is either a register or the MBR. The shifter result may be output to the MBR or any register. The MAR may be written from the ALU’s B side.
The top four words of memory (4092-4095) are wired to a UART. The first two connect to the receiver and the second two connect to the transmitter. The first of each holds an 8-bit character to be outputed in the bottom 8 bits. The second of each holds a 4 bit control flag in its lowest bits. The control bits are (from most to least significant): On, Interrupt, Done, Busy. The control bits are initialized to all zero. If the microprogram sets the On bit, then the component is enabled and stabilizes. The receiver stabilizes to not Done and Busy, while the transmitter stabilizes to Done and not Busy. When the receiver receives a character, it switches to Done and not Busy until the character is read by the CPU. When the program writes a character to the transmit buffer while the transmitter is On, then the transmitter switches to not Done and Busy, until the transmission is finished. The Interrupt flag is currently ignored by both components.
2 mic1 simulator
raco mic1 ‹option› ... ‹microcode-path› ‹memory-image-path› simulates the execution of the MIC-1.
‹microcode-path› must be a path to a file. If the extension is .prom, then it must be in the Microcode Image format. If the extension is .mc, then it must be in the MAL microcode language format and it will be compiled before loading.
‹memory-image-path› must be a path to a file. If the extension is .o, then it must be in the Memory Image format. If the extension is .s, then it must be in the MAC-1 macro-assembly format and it will be compiled before loading.
It accepts the following ‹option›s:
--ll —
simulates at the NAND gate level via compilation to a C program using cc. --lli —
simulates at the NAND gate level via an interpreter. --hl —
simulates at a high-level (default) --pc ‹pc-str› —
specifies the initial value of the register 0, the Program Counter (default: 0) --sp ‹sp-str› —
specifies the initial value of the register 2, the Stack Pointer (default: 1024)
2.1 Microcode Image
A microcode image matches the grammar ‹PROM›.
| ‹PROM› | ::= | ‹Line› ... |
| ‹Line› | ::= | ‹Entry› \n |
| ‹Entry› | ::= | ‹MIR› |
|
| | | # any sequence of character except \n |
| ‹MIR› | ::= | ‹AMUX› ‹COND› ‹ALU› ‹SH› ‹MBR› ‹MAR› ‹RD› ‹WR› ‹ENC› ‹C› ‹B› ‹A› ‹ADDR› |
| ‹AMUX› | ::= | 0 — |
|
| | | 1 — |
| ‹COND› | ::= | 00 — |
|
| | | 01 — |
|
| | | 10 — |
|
| | | 11 — |
| ‹ALU› | ::= | 00 — |
|
| | | 01 — |
|
| | | 10 — |
|
| | | 11 — |
| ‹SH› | ::= | 00 — |
|
| | | 01 — |
|
| | | 10 — |
| ‹MBR› | ::= | 0 — |
|
| | | 1 — |
| ‹MAR› | ::= | 0 — |
|
| | | 1 — |
| ‹RD› | ::= | 0 — |
|
| | | 1 — |
| ‹WR› | ::= | 0 — |
|
| | | 1 — |
| ‹ENC› | ::= | 0 — |
|
| | | 1 — |
| ‹C› | ::= | 4-bit register label |
| ‹B› | ::= | 4-bit register label |
| ‹A› | ::= | 4-bit register label |
| ‹ADDR› | ::= | 8-bit microaddress |
In addition, a microcode image may only contain up to 256 ‹mir› lines.
2.2 Memory Image
A memory image matches the grammar ‹Image›.
| ‹Image› | ::= | ‹Line› ... |
| ‹Line› | ::= | ‹Entry› \n |
| ‹Entry› | ::= | ‹Value› |
|
| | | # any sequence of character except \n |
| ‹Value› | ::= | 16-bit value written using the characters 0 and 1 |
In addition, a memory image may only contain up to 4096 ‹value› lines.
3 mcc microcode compiler
raco mcc ‹microcode-path› compiles MAL microcode language into the Microcode Image format.
‹microcode-path› must be a path to a file in the MAL microcode language format. raco mcc replaces the extension of this path with .prom and writes the corresponding Microcode Image.
3.1 MAL microcode language
While it is possible to directly write in the Microcode Image format, it is extremely error-prone and tedious. MAL provides a convenient way to write microprograms.
MAL supports block comments in between { and }. Labels are sequences of any characters except (,:;).
A MAL program matches the following grammar ‹Program›:
| ‹Program› | ::= | |
|
| | | \n ‹Program› |
|
| | | ‹Instruction› \n ‹Program› |
| ‹Instruction› | ::= | |
|
| | | ‹Component› ; ‹Instruction› |
‹Instruction›s are composed of multiple ‹Component›s. Each ‹Component› determines some fields of the Microcode Image. If two ‹Component›s assign the same field differently, then a compilation error is raised. The following grammar specifies the various ‹Component›s:
| ‹Component› | ::= | mar := ‹BExpr›— |
|
| | | ‹Register› := ‹ShExpr›— |
|
| | | mbr := ‹ShExpr›— |
|
| | | alu := ‹AluExpr›— |
|
| | | if ‹Cond› then goto ‹Label›— |
|
| | | goto ‹Label›— |
|
| | | rd— |
|
| | | wr— |
The remaining nonterminals are specified by the following grammar:
| ‹Cond› | ::= | n — |
|
| | | z — |
| ‹ShExpr› | ::= | ‹AluExpr› — |
|
| | | lshift( ‹AluExpr› ) — |
|
| | | rshift( ‹AluExpr› ) — |
| ‹AluExpr› | ::= | ‹AExpr› + ‹BExpr› — |
|
| | | band( ‹AExpr› , ‹BExpr› ) — |
|
| | | ‹AExpr› — |
|
| | | inv( ‹AExpr› ) — |
| ‹AExpr› | ::= | ‹Register› |
|
| | | mbr |
| ‹BExpr› | ::= | ‹Register› |
| ‹Register› | ::= | pc |
|
| | | ac |
|
| | | sp |
|
| | | sp |
|
| | | ir |
|
| | | tir |
|
| | | 0 |
|
| | | 1 |
|
| | | (-1) |
|
| | | amask |
|
| | | smask |
|
| | | a |
|
| | | b |
|
| | | c |
|
| | | d |
|
| | | e |
|
| | | f |
If a MAL program produces an image greater than 256 instructions, then no error is raised during compilation.
For examples see the Github repository, specifically: fib.mc implements Fibonacci and macro-v1.mc implements an interpreter for compiled MAC-1 macro-assembly.
4 masm macroassembler
raco masm ‹asm-path› compiles MAC-1 macro-assembly into the Memory Image format.
‹asm-path› must be a path to a file in the MAC-1 macro-assembly format. raco masm replaces the extension of this path with .o and writes the corresponding Memory Image.
4.1 MAC-1 macro-assembly
The MAC-1 is a low-level virtual machine implemented by a MIC-1 microprogram. It exposes a single register (AC) to programmers and has an internal state defined by two other registers (PC and SP).
The assembly language supports line comments starting with the ; character. Whitespace is never significant. Literal integers are supported in decimal format. Literal strings compile to packed 16-bit words with early characters in least significant bits.
Labels are any alphanumeric character sequence starting with an alphabetic character and ending in :. A label definition is a label not in an argument position or immediately after a label definition.
The character sequence .LOC followed by a literal nonnegative integer skips the given amount of space in the resulting image, filling it with 1111111111111111.
The following instructions are recognized:
Mnemonic | Encoding | Instruction | Semantics |
LODD ‹Arg› | 0000xxxxxxxxxxxx | Load Direct | AC := Mem[X] |
STOD ‹Arg› | 0001xxxxxxxxxxxx | Store Direct | Mem[X] := AC |
ADDD ‹Arg› | 0010xxxxxxxxxxxx | Add Direct | AC := AC + Mem[X] |
SUBD ‹Arg› | 0011xxxxxxxxxxxx | Subtract Direct | AC := AC - Mem[X] |
JPOS ‹Arg› | 0100xxxxxxxxxxxx | Jump on non-negative | If AC ≥ 0, PC := X |
JZER ‹Arg› | 0101xxxxxxxxxxxx | Jump on zero | If AC = 0, PC := X |
JUMP ‹Arg› | 0110xxxxxxxxxxxx | Jump | PC := X |
LOCO ‹Arg› | 0111xxxxxxxxxxxx | Load Constant | AC := X |
LODL ‹Arg› | 1000xxxxxxxxxxxx | Load Local | AC := Mem[SP + X] |
STOL ‹Arg› | 1001xxxxxxxxxxxx | Store Local | Mem[SP + X] := AC |
ADDL ‹Arg› | 1010xxxxxxxxxxxx | Add Local | AC := AC + Mem[SP + X] |
SUBL ‹Arg› | 1011xxxxxxxxxxxx | Subtract Local | AC := AC - Mem[SP + X] |
JNEG ‹Arg› | 1100xxxxxxxxxxxx | Jump on negative | If AC < 0, PC := X |
JNZE ‹Arg› | 1101xxxxxxxxxxxx | Jump unless zero | If AC ≠ 0, PC := X |
CALL ‹Arg› | 1110xxxxxxxxxxxx | Call | SP := SP - 1; Mem[SP] := PC; PC := X |
PSHI | 1111000000000000 | Push Indirect | SP := SP - 1; Mem[SP] := Mem[AC] |
POPI | 1111001000000000 | Pop Indirect | Mem[AC] := Mem[SP]; SP := SP + 1 |
PUSH | 1111010000000000 | Push | SP := SP - 1; Mem[SP] := AC |
POP | 1111011000000000 | Pop | AC := Mem[SP]; SP := SP + 1 |
RETN | 1111100000000000 | Return | PC := Mem[SP]; SP := SP + 1 |
SWAP | 1111101000000000 | Swap AC & SP | AC :=: SP |
INSP ‹Arg› | 11111100yyyyyyyy | Increment SP | SP := SP + Y |
DESP ‹Arg› | 11111110yyyyyyyy | Decrement SP | SP := SP - Y |
HALT | 1111111100000000 | Halt | Halt processor |
If a MAC-1 program produces an image greater than 4096 instructions, then no error is raised during compilation.
For examples see the Github repository, specifically: fib.s implements Fibonacci and IO_str_and_echo.s implements an echo program.
5 HDL - General Purpose Hardware Description Language
(require mic1/hdl) | package: mic1 |
The implementation contains a general purpose hardware description language that compiles circuits to networks of NAND gates. The network can be simulated in Racket or via C compilation. In the future it may be documented.