Documentation for HMMM
(Harvey Mudd Miniature Machine) Last update: 2025 Quick reference: Table of Hmmm Instructions
Instruction Description Aliases System instructions halt Stop! None read rX Place user input in register rX None write rX Print contents of register rX None nop Do nothing None Setting register data setn rX N Set register rX equal to the integer N (-128 to +127) None addn rX N Add integer N (-128 to 127) to register rX None copy rX rY Set rX = rY mov Arithmetic add rX rY rZ Set rX = rY + rZ None sub rX rY rZ Set rX = rY - rZ None neg rX rY Set rX = -rY None mul rX rY rZ Set rX = rY * rZ None div rX rY rZ Set rX = rY // rZ (integer division; rounds down; no remainder) None mod rX rY rZ Set rX = rY % rZ (returns the remainder of integer division) None Jumps! jumpn N Set program counter to address N None jumpr rX Set program counter to address in rX jump jeqzn rX N If rX == 0, then jump to line N jeqz jnezn rX N If rX != 0, then jump to line N jnez jgtzn rX N If rX > 0, then jump to line N jgtz jltzn rX N If rX < 0, then jump to line N jltz calln rX N Copy addr. of next instr. into rX and then jump to mem. addr. N call Interacting with memory (RAM) pushr rX rY Store contents of register rX onto stack pointed to by reg. rY None popr rX rY Load contents of register rX from stack pointed to by reg. rY None loadn rX N Load register rX with the contents of memory address N None storen rX N Store contents of register rX into memory address N None loadr rX rY Load register rX with data from the address location held in reg. rY loadi, load storer rX rY Store contents of register rX into memory address held in reg. rY storei, store
Table of Contents
Hmmm is implemented as a single program written in Python. By default, hmmm will assemble and run a file written in the Hmmm assembly language. There are options that to assemble a program without executing it, to run a previously assembled program, and to invoke a built-in debugger.
Installing Hmmm
hmmm
Using Hmmm
The Basics
python hmmm
./hmmm
Getting Fancy
python hmmm program.hmmm -o program.hb python hmmm -d program.hb
15 of the 16 registers are interchangeable from a hardware standpoint (although refer to the conventions below). The only special register is r0. When used as a source operand, r0 always provides a zero; when used as a destination it discards the result.
Assembly Binary Description halt 0000 0000 0000 0000 Halt program nop 0110 0000 0000 0000 Do nothing read rX 0000 XXXX 0000 0001 Stop for user input, which will then be stored in register rX (input is an integer from -32768 to +32767).
Prints "Enter number: " to prompt user for input write rX 0000 XXXX 0000 0010 Print the contents of register rX on standard output setn rX, # 0001 XXXX #### #### Load an 8-bit integer # (-128 to +127) into register rX loadr rX, rY 0100 XXXX YYYY 0000 Load register rX from memory word addressed by rY: rX = memory[rY] storer rX, rY 0100 XXXX YYYY 0001 Store contents of register rX into memory word addressed by rY: memory[rY] = rX popr rX rY 0100 XXXX YYYY 0010 Load contents of register rX from stack pointed to by register rY: rY - = 1; rX = memory[rY] pushr rX rY 0100 XXXX YYYY 0011 Store contents of register rX onto stack pointed to by register rY: memory[rY] = rX; rY += 1 loadn rX, # 0010 XXXX #### #### Load register rX with memory word at address # storen rX, # 0011 XXXX #### #### Store contents of register rX into memory word at address # addn rX, # 0101 XXXX #### #### Add the 8-bit integer # (-128 to 127) to register rX copy rX, rY 0110 XXXX YYYY 0000 Set rX = rY neg rX, rY 0111 XXXX 0000 YYYY Set rX = -rY add rX, rY, rZ 0110 XXXX YYYY ZZZZ Set rX = rY + rZ sub rX, rY, rZ 0111 XXXX YYYY ZZZZ Set rX = rY - rZ mul rX, rY, rZ 1000 XXXX YYYY ZZZZ Set rX = rY * rZ div rX, rY, rZ 1001 XXXX YYYY ZZZZ Set rX = rY // rZ mod rX, rY, rZ 1010 XXXX YYYY ZZZZ Set rX = rY % rZ jumpr rX 0000 XXXX 0000 0011 Set program counter to address in rX jumpn n 1011 0000 #### #### Set program counter to address # jeqzn rX, # 1100 XXXX #### #### If rX = 0 then set program counter to address # jnezn rX, # 1101 XXXX #### #### If rX ≠ 0 then set program counter to address # jgtzn rX, # 1110 XXXX #### #### If rX > 0 then set program counter to address # jltzn rX, # 1111 XXXX #### #### If rX < 0 then set program counter to address # calln rX, # 1011 XXXX #### #### Set rX to (next) program counter, then set program counter to address #
The debug mode prints information after executing each instruction, showing what instruction was just executed and where that instruction was found in memory (what the program counter was). It also prints the debug prompt. Any unrecognized input at the debug prompt causes the simulator to step one instruction forward. Recognized commands include 'c' or 'continue', 'd' or 'dump', 'h' or 'help', 'p' or 'print', 'q' or 'quit', and 'r' or 'run'.
Command Effect continue Causes the debugger to run through the rest of the program without prompting for debugging commands, but continuing to print debugging information. dump Immediately prints the contents of memory, printing the code lines first (one per line, in binary) followed by the numeric contents of the rest of memory in 6 columns, and then asks for another debugging command. help Prints a short summary of the debug commands and returns to the prompt. print Prints the contents of the registers in a single column and returns to the prompt. quit Causes the program to exit immediately. run Causes the program to continue running as if it had been invoked with debug mode off: no debugging information is printed and no further prompts are given.
# program title # author and date # descriptive comment 0 read r1 # read dividend from the user 1 write r1 # echo the input 2 read r2 # read divisor from the user 3 jeqzn r2, 7 # jump to 7 if trying to divide by 0 4 div r3, r1, r2 # divide user's parameters 5 write r3 # print the result 6 halt 7 setn r3, 0 # 0 is the result for division by 0 8 write r3 # print the result 9 halt
hmmm
---------------------- | ASSEMBLY SUCCESSFUL | ---------------------- 0: 0000 0001 0000 0001 0 read r1 # read dividend from the us 1: 0000 0001 0000 0010 1 write r1 # echo the input 2: 0000 0010 0000 0001 2 read r2 # read divisor from the use 3: 1100 0010 0000 0111 3 jeqzn r2, 7 # jump to 7 if trying to di 4: 1001 0011 0001 0010 4 div r3, r1, r2 # divide user's paramete 5: 0000 0011 0000 0010 5 write r3 # print the result 6: 0000 0000 0000 0000 6 halt 7: 0001 0011 0000 0000 7 setn r3, 0 # 0 is the result for divis 8: 0000 0011 0000 0010 8 write r3 # print the result 9: 0000 0000 0000 0000 9 halt Enter number (q to quit): 42 42 Enter number (q to quit): 6 7
0 read r1 1 write r1 2 read r2 # read r2 3 jeqzn r2, 7 4 div r3 r1, r2 5 write r3 6 halt # end 7 setn r3 0 8 write r3 9 halt
---------------------- | ASSEMBLY SUCCESSFUL | ---------------------- 0: 0000 0001 0000 0001 0 read r1 1: 0000 0001 0000 0010 1 write r1 2: 0000 0010 0000 0001 2 read r2 # read r2 3: 1100 0010 0000 0111 3 jeqzn r2, 7 4: 1001 0011 0001 0010 4 div r3 r1, r2 5: 0000 0011 0000 0010 5 write r3 6: 0000 0000 0000 0000 6 halt # end 7: 0001 0011 0000 0000 7 setn r3 0 8: 0000 0011 0000 0010 8 write r3 9: 0000 0000 0000 0000 9 halt Enter number (q to quit): 42 42 Enter number (q to quit): 7 6
# program title # program title # author and date # descriptive comment 0 read r1, # trailing characters are not allowed 1 write[r1] # no grouping symbols allowed 2 read r2, r3 # too many arguments here 3 jeqzn r2, r7 # second argument must be a number 4 div r3, r1r2 # arguments must be separated 5 write 3 # write argument must be a register 5 halt # line number is incorrect 7 setn r3, 128 # maximum number for setn and addn commands is 127 # (min is -128) 9 writer3 # instruction must be separated from argument 10halt # instruction must be separated from line number
ARGUMENT ERROR: WRONG NUMBER OF ARGUMENTS. DETECTED 2 ARGUMENTS, EXPECTED 1 ARGUMENTS read r1, SYNTAX ERROR ON LINE 1: 1 write[r1] # no grouping symbols allowed ARGUMENT ERROR: WRONG NUMBER OF ARGUMENTS. DETECTED 2 ARGUMENTS, EXPECTED 1 ARGUMENTS read r2, r3 ARGUMENT ERROR: 'r7' IS NOT A VALID NUMBER. ARGUMENT ERROR: WRONG NUMBER OF ARGUMENTS. DETECTED 2 ARGUMENTS, EXPECTED 3 ARGUMENTS div r3, r1r2 REGISTER ERROR: '3' IS NOT A VALID REGISTER. BAD LINE NUMBER AT LINE 6: LINE NUMBER: 5 EXPECTED 6 ARGUMENT ERROR: '128' IS OUT OF RANGE FOR THE ARGUMENT. OPERATION ERROR: 'writer IS NOT A VALID OPERATION. SYNTAX ERROR ON LINE 9: 10halt # instruction must be separated from line number ***** ASSEMBLY TERMINATED UNSUCCESSFULLY ***** ASSEMBLY RESULTS: 0: ***ARGUMENT ERROR HERE*** 0 read r1, # trailing characters are n 1: ***SYNTAX ERROR HERE*** 1 write[r1] # no grouping symbols allow 2: ***ARGUMENT ERROR HERE*** 2 read r2, r3 # too many arguments here 3: ***ARGUMENT ERROR HERE*** 3 jeqzn r2, r7 # second argument must be 4: ***ARGUMENT ERROR HERE*** 4 div r3, r1r2 # arguments must be separa 5: ***REGISTER ERROR HERE*** 5 write 3 # write argument must be a 6: ***BAD LINE NUMBER HERE*** 5 halt # line number is incorrect 7: ***ARGUMENT ERROR HERE*** 7 setn r3, 128 # maximum number for s 8: ***OPERATION ERROR HERE*** 9 writer3 # instruction must be separ 9: ***SYNTAX ERROR HERE*** 10halt # instruction must be separ ***** ASSEMBLY FAILED, SEE ABOVE FOR ERRORS *****
Although it may not be obvious from the example above, the assembler will stop trying to assemble each line of code as soon as it finds an error. Thus, if a line of code has multiple errors in it, only one error will be reported. Once that error is fixed, the next error on that line will be reported if it is still there.
hmmm
cs5help