Little man computer
The Little Man Computer is an instructional model of a computer, created by Dr. Stuart Madnick in 1965. The LMC is generally used to teach students, because it models a simple von Neumann architecture computer—which has all of the basic features of a modern computer. It can be programmed in machine code or assembly code.
The LMC model is based on the concept of a little man shut in a closed mail room. At one end of the room, there are 100 mailboxes, numbered 0 to 99, that can each contain a 3 digit instruction or data. Furthermore, there are two mailboxes at the other end labeled INBOX and OUTBOX which are used for receiving and outputting data. In the center of the room, there is a work area containing a simple two function calculator known as the Accumulator and a resettable counter known as the Program Counter. The Program Counter holds the address of the next instruction the Little Man will carry out. This Program Counter is normally incremented by 1 after each instruction is executed, allowing the Little Man to work through a program sequentially. Branch instructions allow iteration and conditional programming structures to be incorporated into a program. The latter is achieved by setting the Program Counter to a non-sequential memory address if a particular condition is met.
As specified by the von Neumann architecture, each mailbox contains both instructions and data. Care therefore needs to be taken to stop the Program Counter from reaching a memory address containing data - or the Little Man will attempt to treat it as an instruction. One can take advantage of this by writing instructions into mailboxes that are meant to be interpreted as code, to create self-modifying code. To use the LMC, the user loads data into the mailboxes and then signals the Little Man to begin execution, starting with the instruction stored at memory address zero. Resetting the Program Counter to zero effectively restarts the program, albeit in a potentially different state.
Execution cycle
To execute a program, the little man performs these steps:- Check the Program Counter for the mailbox number that contains a program instruction
- Fetch the instruction from the mailbox with that number. Each instruction contains two fields: An opcode and the address field.
- Increment the Program Counter
- Decode the instruction. If the instruction utilises data stored in another mailbox then use the address field to find the mailbox number for the data it will work on, e.g. 'get data from mailbox 42')
- Fetch the data
- Execute the instruction based on the opcode given
- Branch or store the result
- Return to the Program Counter to repeat the cycle or halt
Commands
Instructions
Some LMC simulators are programmed directly using 3-digit numeric instructions and some use 3-letter mnemonic codes and labels. In either case, the instruction set is deliberately very limited to simplify understanding. If the LMC uses mnemonic codes and labels then these are converted into 3-digit numeric instructions when the program is assembled.The table below shows a typical numeric instruction set and the equivalent mnemonic codes.
Numeric code | Mnemonic code | Instruction | Description | ||||||||||||||||||||||||||||||||||||
1xx | ADD | ADD | Add the value stored in mailbox xx to whatever value is currently on the accumulator.ExamplesUsing Numeric Instruction CodesThis program is written just using numeric codes. The program takes two numbers as input and outputs the difference. Notice that execution starts at Mailbox 00 and finishes at Mailbox 07. The disadvantages of programming the LMC using numeric instruction codes are discussed below.
Using Mnemonics and Labelsis a low-level programming language that uses mnemonics and labels instead of numeric instruction codes. Although the LMC only uses a limited set of mnemonics, the convenience of using a mnemonic for each instruction is made apparent from the assembly language of the same program shown below - the programmer is no longer required to memorize a set of anonymous numeric codes and can now program with a set of more memorable mnemonic codes. If the mnemonic is an instruction that involves a memory address then a label is used to name the memory address.INP STA FIRST INP STA SECOND LDA FIRST SUB SECOND OUT HLT FIRST DAT SECOND DAT LabelsWithout labels the programmer is required to manually calculate mailbox addresses. In the numeric code example, if a new instruction was to be inserted before the final HLT instruction then that HLT instruction would move from address 07 to address 08. Suppose the user entered 600 as the first input. The instruction 308 would mean that this value would be stored at address location 08 and overwrite the 000 instruction. Since 600 means "branch to mailbox address 00" the program, instead of halting, would get stuck in an endless loop.To work around this difficulty, most assembly languages combine the mnemonics with labels. A label is simply a word that is used to either name a memory address where an instruction or data is stored, or to refer to that address in an instruction. When a program is assembled:
Labels are therefore used to:
INP OUT // Initialize output LOOP BRZ QUIT // If the accumulator value is 0, jump to the memory address labeled QUIT SUB ONE // Label this memory address as LOOP, The instruction will then subtract the value stored at address ONE from the accumulator OUT BRA LOOP // Jump to the memory address labeled LOOP QUIT HLT // Label this memory address as QUIT ONE DAT 1 // Store the value 1 in this memory address, and label it ONE This program will take a user input, square it, output the answer and then repeat. Entering a zero will end the program. . START LDA ZERO // Initialize for multiple program run STA RESULT STA COUNT INP // User provided input BRZ END // Branch to program END if input = 0 STA VALUE // Store input as VALUE LOOP LDA RESULT // Load the RESULT ADD VALUE // Add VALUE, the user provided input, to RESULT STA RESULT // Store the new RESULT LDA COUNT // Load the COUNT ADD ONE // Add ONE to the COUNT STA COUNT // Store the new COUNT SUB VALUE // Subtract the user provided input VALUE from COUNT BRZ ENDLOOP // If zero, branch to ENDLOOP BRA LOOP // Branch to LOOP to continue adding VALUE to RESULT ENDLOOP LDA RESULT // Load RESULT OUT // Output RESULT BRA START // Branch to the START to initialize and get another input VALUE END HLT // HALT - a zero was entered so done! RESULT DAT // Computed result COUNT DAT // Counter ONE DAT 1 // Constant, value of 1 VALUE DAT // User provided input, the value to be squared ZERO DAT // Constant, value of 0 Note: If there is no data after a DAT statement then the default value 0 is stored in the memory address. In the example above, depends on undefined behaviour, as COUNT-VALUE can be negative, after which the ACCUMULATOR value is undefined, resulting in BRZ either branching or not. To make the code compatible with the specification, replace: ... LDA COUNT // Load the COUNT ADD ONE // Add ONE to the COUNT STA COUNT // Store the new COUNT SUB VALUE // Subtract the user provided input VALUE from COUNT BRZ ENDLOOP // If zero, branch to ENDLOOP ... with the following version, which does VALUE-COUNT instead of COUNT-VALUE, making sure the accumulator never underflows: ... LDA COUNT // Load the COUNT ADD ONE // Add ONE to the COUNT STA COUNT // Store the new COUNT LDA VALUE // Load the VALUE SUB COUNT // Subtract COUNT from the user provided input VALUE BRZ ENDLOOP // If zero, branch to ENDLOOP ... Another example is a quine, printing its own machine code : LOAD LDA 0 // Load position 0 into the accumulator. This line will be modified on each loop to load the next lines into the accumulator OUT // Output the accumulator's value. The accumulator's value will be the line that was just loaded SUB ONE // Subtract 1 from the value in the accumulator. This is so we can do the BRZ in the next step to see if we are on the last line in the program BRZ ONE // If the previous subtraction has made the accumulator 0, then branch to position ONE LDA LOAD // Load the LOAD position into the accumulator, this is in preparation to increment the address digits for this position ADD ONE // Increment the position digits for the LOAD line. The value currently in the accumulator would, if read as an instruction, load the next line into the accumulator, compared to the last line loaded STA LOAD // Store the newly incremented LOAD line back in the LOAD position BRA LOAD // Return to the beginning of the loop ONE DAT 1 // The variable 1. If read as an instruction, this will be interpreted as HLT/COB and will end the program This quine works using self-modifying code. Position 0 is incremented by on each loop, outputting that line's code, until the code it is outputting is 1, at which point it branches to the ONE position. The ONE position begins with a 0, so it is interpreted as a HALT/COB instruction. Simulators |