I am working at this coding challenge:
Write a program for the Little Man Computer that allows the user to manage a list of values. It should start with an empty list and then process input as follows:
If the input is:
- less than 100: add this value to a list, unless the list already has 10 values, in which case the value is ignored
- 995: make the list empty
- 996: output the number of values the list currently has
- 997: output each value the list currently has, in the order they were added to it
- 998: output each value the list currently has, in reversed order
- 999: end the program
- Any other value is ignored
The processing of input values continues as long as the input value is not 999.
I'm having issues getting the code to print the stored list in forward order when 997 is input. I think I may have the ADD
and SUB
instructions confused. I also can't get the stored list to reset correctly when 995 is input.
Everything else I was able to program correctly.
Below is my code:
START INP
STA TEMP
SUB NINES
BRZ end
LDA TEMP
SUB EIGHT
BRZ PRIT
lda temp
sub seven
brz printf
LDA TEMP
SUB SIX
BRZ DOOUT
LDA TEMP
SUB FIVE
BRZ RESET
LDA COUNT
SUB TEN
BRZ START
LDA TEMP
SUB HUND
BRP START
SIT LDA SINST
ADD COUNT
STA SLOC
LDA TEMP
SLOC DAT 0
LDA COUNT
ADD ONE
STA COUNT
BRA START
PRIT LDA COUNT
BRZ END
PRINTR LDA LINST
ADD COUNT
SUB ONE
STA LDIT
LDIT DAT 0
OUT
LDA COUNT
SUB ONE
STA COUNT
BRZ END
BRA PRINTR
---PRINTF LDA LINST
ADD COUNT
add ONE
STA LDIT
LDIT DAT 0
OUT
LDA COUNT
SUB ONE
STA COUNT
BRZ END
BRA PRINTF
doout lda count
out
bra start
reset lda zero
sta count
bra start
END HLT
TEMP DAT 0
COUNT DAT 0
ONE DAT 1
TWO DAT 2
TEN DAT 10
HUND DAT 100
SINST DAT 380
LINST DAT 580
five dat 995
six dat 996
seven dat 997
eight dat 998
NINES DAT 999
The issues in your program can be categorised in:
A good simulator should not accept the following errors:
PRINTF
label is not defined.
There is a brz printf
instruction, but that label is not defined, since ---
makes it a comment. Obviously that ---
needs to be removed for the label reference to be valid.
LDIT
label is duplicate:
The label LDIT is defined twice. This will have unreliable behaviour. A good simulator should give an error message about this, but other simulators will just take one definition and ignore the duplicates. Either way, the intention of the program is that one STA LDIT
uses the first LDIT location and the second STA LDIT
uses the second LDIT location. If anything, this will not be what happens. So rename one of the two labels, and adjust one of the STA LDIT
instructions accordingly.
zero
label is undefined:
The code to reset the counter refers to an undefined label zero
. Again, good simulators will produce an error when loading the program, but others may silently use mailbox 0 instead, which leads to undesirable behaviour. So define the zero
label as
zero DAT 0
The code for the printing of the list, either in forward or backward direction, both change the value of COUNT
, but this way the code will loose what is the size of the list! For instance, if after such a traversal you would choose action 996 -- i.e. to query the size of the list -- it will not give the correct result. The solution is to use a different variable for traversing through the list, and leave COUNT
untouched. COUNT
should only be changed when you add a value to the list, or when you reset the list.
The code for printing ends by jumping to END
, but it seems that the user should be allowed to continue with another "menu" option, so instead of jumping to END
it should jump to START
. The only reason to jump to END
should be because the user inputs the choice 999.
For forward printing you should not start by adding COUNT
to the dynamic load instruction, as you need to start with the first element of the list, not the last. So don't do ADD COUNT
nor ADD ONE
before printing the first time. Instead, increment the dynamic load instruction until its difference with the original load instruction -- i.e. calculate the relative offset in the list -- is at least COUNT
.
The LMC specification has a particular oddity: it does not define what the accumulator's value will be when a subtraction would lead to a negative result. The accumulator is not able to store negative values, only flag a negative result. By consequence it is not safe to do BRZ
when you have just performed a SUB
instruction that could lead to a negative value (because strangely enough, a simulator might react with a zero value in the accumulator when a subtraction would be negative). In short, if you can, prefer using BRP
instead of BRZ
, or least use BRP
before using BRZ
. NB: even teachers on the subject are not always aware of this.
An LMC has a reset "handle", which sets the program counter back to the start of the program. When that happens you will want to start with a clean slate, and have COUNT
reset to 0. So add the resetting code at the very top of your program.
This will fix the semantic issues in your program.
I would suggest that you use more meaningful names. doout
is not very enlightening, as your program is designed to output different things (list in forward order, list in backward order, the size of the list). So for instance, use output_size
instead of doout
. Names like LDIT
, PRIT
, ... are quite cryptic. A program will be much easier to read and understand when it uses descriptive names, without abbreviations that only you will understand.
The dynamic instructions are based on SINST
(store instruction) and LINST
(load instruction), which are hardcoded as:
SINST DAT 380
LINST DAT 580
But it is a pity that you have hardcoded them like that. First of all, they assume that mailbox 80 is free for storing the list, and secondly it requires knowledge of the opcodes of these instructions (3 and 5). Yet, with a good assembler you shouldn't have to do this. So I suggest to do this instead:
store_instruction STA list
load_instruction LDA list
...and define list
with a DAT
instruction at the very bottom of your code (as that is where there are mailboxes available). I would even add dummy DAT
lines after it, so to be very clear about the mailboxes that are part of that list -- it just makes it easier for someone to understand the code:
list DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
This way, the list may not be stored at mailbox 80, but we don't care. We leave it to the assembler to assign the next free mailbox for our list. It may also look strange to have STA
and LDA
instructions in the middle of your "data section", where they will never be executed, but the principle of the LMC architecture (Von Neumann architecture) is that code and data use the same memory, so this is fine. The LDA
and STA
instructions will be copied from there in to the actual program.
Taking all of the above into account, the program could look like this:
#input:1 2 3 997 998 999
clear_list LDA zero # Start by resetting the list
STA list_size
start INP
STA input
SUB menu_nine
BRP end
LDA input
SUB menu_eight
BRP output_reversed
LDA input
SUB menu_seven
BRP output_forward
LDA input
SUB menu_six
BRP output_size
LDA input
SUB menu_five
BRP clear_list
LDA list_size
SUB max_list_size
BRP start
LDA input
SUB input_limit
BRP start
LDA store_instruction
ADD list_size
STA store
LDA input
store DAT 0
LDA list_size
ADD one
STA list_size
BRA start
output_reversed LDA load_instruction
ADD list_size
loop_reversed SUB one
STA load_reversed
SUB load_instruction # are we still within the list?
BRP load_reversed # yes, continue printing
BRA start
load_reversed DAT 0
OUT
LDA load_reversed # decrement the dynamic LDA instruction
BRA loop_reversed
output_forward LDA load_instruction
loop_forward STA load_forward
SUB load_instruction # get relative offset in the list
SUB list_size # are we still within the list?
BRP start # no, stop printing
load_forward DAT 0
OUT
LDA load_forward # increment the dynamic LDA instruction
ADD one
BRA loop_forward
output_size LDA list_size
OUT
BRA start
end HLT
# constants
zero DAT 0
one DAT 1
two DAT 2
max_list_size DAT 10
input_limit DAT 100
load_instruction LDA list
store_instruction STA list
menu_five DAT 995
menu_six DAT 996
menu_seven DAT 997
menu_eight DAT 998
menu_nine DAT 999
# variables
input DAT 0
list_size DAT 0
list DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
<script src="https://cdn.jsdelivr.net/gh/trincot/lmc@v0.816/lmc.js"></script>
You can run the code right here, using this snippet. Click Run Code Snippet to activate a LMC simulator, and then use the panel at the right side to interact with it.