The Little Man Computer (LMC)
Assembly Language programming using the LMC Simulator.
- The LMC instruction set
- A summary table of the LMC instruction set
- Input and Output (INP and OUT)
- Using labels
- Load and save (LDA and STA)
- Add and subtract (ADD and SUB)
- Branching (BRZ, BRA and BRP)
- Decisions (BRP and BRA)
- Replicating an IF/ELSE/ENDIF conditional programming structure
- Replicating a WHILE/ENDWHILE iteration (loop) programming structure
A Little Man Computer (LMC) is a simulator which has many of the basic features of a modern computer that uses the Von Neumann architecture (a central processing unit consisting of an arithmetic logic unit and registers, a control unit containing an instruction register and program counter, input and output mechanisms and RAM to store both data and instructions).
The LMC is based on the idea of a 'Little Man' acting like the control unit of a CPU, fetching instructions from RAM, decoding and executing them as well as managing the input and output mechanisms.
The two versions on this website can be programmed by using a basic set of 10 assembly code instructions which are then assembled into machine code (although in decimal not binary).
Understanding the LMC simulator
- The 100 memory addresses in the computer memory are numbered 0 to 99 and can each contain a 'machine code' instruction or data.
- Each assembly language instruction is made up of a 3 letter mnemonic (which represents the operation code), usually followed by the memory address of the data the CPU is to act on (this is called absolute memory addressing).
- Pressing the Assemble Program button translates the assembly language instructions into 'machine code' and loads them into RAM. it also resets the Program Counter to zero.
- The Input box allows the user to enter numerical data (-999 to 999) while a program is running and load it into the accumulator.
- The Output box can output the contents of the accumulator while a program is running.
- A RAM memory address that is used to store data can be given a meaningful label. Data can also be stored in these named address locations.
- The results of any ADD or SUBTRACT instructions are stored in the accumulator .
- The Program Counter stores the memory address of the instruction being carried out. It will automatically increment by 1 after each instruction is completed.
- If the CPU receives an non-sequential instruction to branch (BRP, BRP or BRZ) then the Program Counter is set to the memory address of that instruction.
- Branch instructions are set to branch to a labelled memory location.
- To restart a program, the Program Counter is reset to 0.
When assembled, each assembly code instruction is converted into a 3 digit 'machine code' instruction (1 digit for the instruction and 2 for the memory address). The 3 digit 'machine code' instructions are then loaded into RAM, starting at memory address 0.
Any data is also loaded into memory at the memory address corresponding to the location of the data in the program (i.e. if the 5th line of the assembly language program was data then this data would be loaded into address 4, because memory address start at 0 not 1)
The 'Little Man' can then begin execution, starting with the instruction in RAM at memory address 0.
The 'Little Man' performs the following steps to execute a program:
- Check the Program Counter so it knows the memory address to look at.
- Fetch the instruction from the memory address that matches the program counter.
- Increment the Program Counter (so that it contains the memory address of the next instruction).
- Decode the instruction (includes finding the memory address for any data it refers to).
- If required by the instruction code, fetch the data from the memory address found in the previous step.
- Execute the instruction and if necessary set the Program Counter to match any branch instructions.
Some programming tasks to try using the LMC
- Write a program that will prompt for 2 numbers, subtract the first from the second and output the answer, then subtract the second from the first and output the answer.
- Write a program to output the numbers 1 to 10 in ascending order.
- Write a program to output the numbers 1 to 10 in descending order.
- Write a program to input a number then count up to that number in steps of 2, outputting the sequence.
- Write a program that will input two numbers and multiply them.
- Extend the program above that it will let the user repeatedly input and multiply pairs of numbers, only stopping if a zero is entered.
- Extend the program above so it will let the user enter either or both numbers as negative values.
- Write a program that will prompt for 2 numbers and check if they are the same. If they are then the program should output the number 1. If they are not then the program should output the number 0.
- Write a program that will input an number, then output the square of the number.
- Modify the squares program above so if an input above 31 is entered then it will output a zero.
- Modify the squares program above so it will also square negative number inputs.
- Write a program to input 3 numbers and then output the highest.
- Write a program to input 3 numbers and then output the highest AND the lowest.
- Write a program to divide one number by another.
- Modify the previous program so it generates a decimal answer rather than a remainder.
A simple way to do this is to multiply the remainder by 10 and then divide this number by the original divisor. If this is repeated it will generate the result of the division to unlimited decimal places. This can be done in 42 lines of LMC code.
- Write a program to input a number and then outputs:
a) the number
b) the integer square root
c) the remainder.
A simple way to calculate an integer square root is to count how many times increasing odd numbers can be subtracted from the starting value.
32-1=31 -> 31-3=28 -> 28-5=23 -> 23-7=16 -> 16-9=7 -> 7-11<0 1 2 3 4 5 -> SQR(32) = 5 remainder 7
- Write a program to calculate PI to 6 decimal places (3.141592).
This can be done by 'dividing' 355 by 113 to get the integer 3. The remainder is 'multiplied' by 10 and then 'divided' again by 113 to get the first decimal place. This is then repeated 6 times. 355/113 is used as there is no "better approximation" among all fractions (P/Q) with denominators less than 30,000.
Factors to consider when writing programs in assembly language/machine code using the LMC
- How easy is it to follow programs with many conditional instructions or iteration loops?
- What are the issues when trying to create program structures such as subroutines, for/next loops, while/endwhile loops, repeat/until loops or if/then/else/endif decisions?
- Is the fact that there are only 100 address locations in RAM to store both the program instructions and the data an issue?
- Does it matter that data can normally only be placed in the memory address of the DAT statement?
- Does that fact that instructions and data are both stored together in RAM cause any issues when writing a program?
- Can the equivalent of an array be created to store and process a series of values as a program runs?
- Is it easy to carry out non-integer arithmetic?
- What if you needed higher level functions such as square-roots, trigonometry or needed more complex calculations that would require brackets and the use of AND/OR/NOT?
- What if you wanted to incorporate text or basic graphics in your program?
Demonstration programs using the LMC
A sorting program, input values are stored from memory address 80 onwards (therefore a maximum of 20 numbers can be sorted). An input of 0 starts the sort.
This program inputs a value (greater or equal to zero) and outputs which triangular number it is, or 0 if it is not a triangular number. For example, if the input is 15 the output will be 5 (15 is the 5th triangular number) and if the input is 7 the output will be 0 (7 is not a triangular number).
The triangular numbers are as follows:
1 = 1
3 = 1 + 2
6 = 1 + 2 + 3
10 = 1 + 2 + 3 + 4
15 = 1 + 2 + 3 + 4 + 5
21 = 1 + 2 + 3 + 4 + 5 + 6
this program illustrates the LMC equivalent of an array, storing each successive INPUT in successive memory locations and counting (the equivalent of the array index) how many numbers are input. The numbers are then OUTPUT in the same order they were entered when a zero is entered.
To do this the program has to alter some instructions as it runs. It does this by treating them as data (they are loaded, altered and saved) and in this program the 'data' values 350 and 550 are treated as STA (store) and LDA (load) instructions respectively by the LMC. This 'data' is altered as the program runs by adding a 1 to it so that it stores INPUT values in memory locations 50 onwards.
This program illustrates one of the issues with memory being used for both instructions and data. As the program runs, numbers are INPUT and stored in memory locations 0 onwards. When memory location 10 is reached the instructions in the main program loop start to be overwritten with data, causing the program to stop running correctly as the LMC tries to interpret the data as an instruction.