1

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
trincot
  • 317,000
  • 35
  • 244
  • 286
julio11
  • 27
  • 8
  • You should describe what the program is to do. Surely, prefixing a label with `---` is making the program invalid... – trincot Jul 18 '21 at 08:09

1 Answers1

1

The issues in your program can be categorised in:

  1. Issues related to labels that a good simulator should detect before running your program
  2. Issues related to program logic, that make the program produce the wrong output
  3. Issues related to code style

1. Labels

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
    

2. Logic

  • 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.

3. Code style

  • 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.

Corrected 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.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • Much help! Whenever I load it into the RAM on a LMC simulator, there is an error on line 45. Is it because of how the list variables are programed? – julio11 Jul 19 '21 at 02:01
  • Which simulator? – trincot Jul 19 '21 at 05:30
  • 1
    Some simulators cannot deal with lines that only have a comment, like `# variables` or `# constants`, or the very first line. If that is the case, remove those lines. Or use a simulator that can deal with that, like [mine](https://trincot.github.io/lmc.html) – trincot Jul 19 '21 at 10:44
  • 1
    Can you let me know which simulator you are using, and whether removing the comment lines solved your problem? – trincot Jul 20 '21 at 06:32
  • 1
    I used 101computing LMC simulator to test it. It was the comment lines and fixing it resolved the problem. – julio11 Jul 20 '21 at 12:58