Harry Barry
Harry Barry

Reputation: 25

How to correctly reset the NEG flag in a LMC without storing said variable then loading it again?

My given task is to find the mean of the sum of 3 inputs rounded down using LMC. To which I had the following code:

    LDA zro
    STO r
    STO cnt
    LDA t
    STO abc
lp  IN  
    STO a 
div LDA a
    SUB t
    ADD r   
    BRP x
    BRZ x
    BR  y
x   STO a    
    LDA cnt
    ADD one
    STO cnt   
    LDA zro
    STO r
    BR  div
y   ADD t
    STO r 
    LDA abc
    SUB one
    STO abc
    BRZ out
    BR  lp
out LDA cnt
    OUT
    HLT
a   DAT 000
t   DAT 003
cnt DAT 000
one DAT 001
abc DAT 003
r   DAT 000
zro DAT 000

which works for 99% of inputs 0-999, save for a few like:

For these inputs, the program produces a value one less than expected. This is because of a bug where the NEG flag doesn't reset until a new value is loaded.

For example with 4,4,2 during the last input loop where 2 is input (abc=1), we have a=2; 2 - 3 = (999 NEG) + 2 = (1 NEG) which should've been just 1. But the NEG flag hasn't been reset leading the branching into the wrong line (y instead of x). Therefore cnt is one less than expected.

My question then is how do you fix that (with minimal added instructions)?

Upvotes: 1

Views: 107

Answers (1)

trincot
trincot

Reputation: 350310

Indeed, with LMC it is not good to perform an ADD immediately after a SUB because of the effect on the negative flag and on the accumulator. Not only could ADD clear the flag or not (depending on the simulator), the accumulator value after a SUB is not defined either, although most LMC simulators will use modular arithmetic (or will allow the accumulator to really have a negative value), this behaviour cannot be relied on.

The solution is to first check with BRP whether the SUB didn't overflow below zero, and then -- if it did -- load the accumulator again with the original value before the subtraction was done. It is the most reliable way to proceed (in terms of portability of the code).

Some other comments:

  • BRZ has little sense when it follows immediately after a BRP as the latter will branch also when the accumulator's value is zero.

  • The name out for a label is ambiguous: some LMC simulators might interpret it as the instruction OUT and will not be able to parse the program.

  • The names r, t, x, y, cnt, ...etc are not very descriptive. I wouldn't abbreviate anything and just use longer labels: it will improve the readability of the code.

  • I would not add the remainder repeatedly to the current value during the division process (in x), as in most cases the remainder will be 0. You could do this in the case where the last subtraction was performed (the y case), and only then reset it to zero, and perform one more iteration of the division loop (as now the current value might be between 3 and 5).

  • If you swap the blocks for x and y you can save one BR instruction.

So here is the modified code -- you can run it here:

#input: 4 4 2
           LDA zero
           STO remainder
           STO average
           LDA three
           STO inputcount
getnext    IN
           STO value
divide     LDA value
           SUB three
           BRP positive
           LDA remainder # ignore the subtraction of 3
           BRZ continue  # branch if no remainder
           ADD value # now we have no problem with NEG flag
           STO value # remainder is added
           LDA zero
           STO remainder
           BR  divide # repeat one more time
positive   STO value
           LDA average
           ADD one
           STO average
           BR  divide
continue   LDA value
           STO remainder
           LDA inputcount
           SUB one
           STO inputcount
           BRZ output
           BR  getnext
output     LDA average
           OUT
           HLT
value      DAT 0
average    DAT 0
inputcount DAT 3
remainder  DAT 0
zero       DAT 0
one        DAT 1
three      DAT 3



<script src="https://cdn.jsdelivr.net/gh/trincot/[email protected]/lmc.js"></script>

Upvotes: 2

Related Questions