Programming arrays in LMC - arrays

I am working on this challenge:
The program needs to accept a sequence of integers. It ends with number 999. The integers (except 999) are placed in a list.
The integers must be less than or equal to 99. Any inputs greater than 99 are not placed in the list.
If the input would have more than ten numbers, only the first ten are stored.
999 is not part of the output.
I don't know how to limit the list length to ten (10) numbers. Also I don't know how to output the list in reverse order.
This is my code:
start INP
STA temp
SUB big
BRZ doout
LDA temp
SUB hundred
BRP start
sub one
STA N
xx STA ARR
LDA xx
add one
sta xx
BRA start
doout HLT
temp dat 0
big dat 999
hundred dat 100
ARR dat
one dat 1
N dat 10

The xx in your program show that you haven't taken the hint from How can I store an unknown number of inputs in different addresses in LMC (little-man-computer)?
It explains how you can have self-modifying code to walk through an array -- either to store values or to load them.
In your attempt there is no section that deals with outputting.
For the start section of the program I would actually suggest to subtract first the 100 and then 899 (which amounts to 999). That way you can keep the (reducing) input in the accumulator without having to restore it.
Also, due to an ambiguity in the specification of LMC, it is not entirely "safe" to do a BRZ right after a SUB (this is because the content of the accumulator is undefined/unspecified when there is underflow, so in theory it could be 0). You should always first do a BRP before doing a BRZ in the branched code. However, as input cannot be greater than 999, a BRP is enough to detect equality.
For the self modifying part, you can set an end-marker in your array data section, and define the LDA and STA instructions that would read/store a value at the end of the array. Whenever your code has that exact instruction, you know you have reached the end.
Here is how it can work:
LDA store # Initialise dynamic store instruction
STA dyna1
loop INP
dyna1 STA array
SUB toobig
BRP skip
LDA dyna1
ADD one
STA dyna1
SUB staend
BRP print
BRA loop
skip SUB trailer
BRP print # Safer to do BRP than BRZ
BRA loop # Input was less than 999
print LDA dyna1 # Convert dynamic store instruction
SUB store # ... to index
ADD load # ... to load instruction
STA dyna2
loop2 LDA dyna2
SUB one
STA dyna2
SUB load
BRP dyna2
end HLT # all done
dyna2 LDA array
OUT
BRA loop2
store STA array
load LDA array
staend STA after
one DAT 1
toobig DAT 100
trailer DAT 899
array DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
after DAT
<script src="https://cdn.jsdelivr.net/gh/trincot/lmc#v0.816/lmc.js"></script>
As you can see (while running the script here), the instructions at dyna1 and dyna2 are modified during the execution of the loop they are in.

Related

How can you reverse a list in LMC language?

I have a list in LMC and I would like to try to reverse it like so :
tab dat 111
dat 222
dat 333
dat 444
dat 555
tab dat 555
dat 444
dat 333
dat 222
dat 111
-I tried to find the right element first by using the table size
-Then I substracted 200 from that instruction so that the instructions it turns from 520 -> 320.
-Essentially I changed the instruction from LOAD what is currently in the accumulator to the 20th square in the RAM to STORE what is currently in the accumulator to the 20th sqaure in the RAM
-Then I loaded the content tab at index 0 into the accumulator (111) then saved it in the last index
-I dont know what I have to do afterwards
-I feel like my approche to the problem is somehow wrong
right_el lda size
sub one
sta size
lda load
add size
sub 2hund
sta save
load lda tab
bra save
inc lda load
add one
sto load
bra load
save dat
bra right_el
left_el dat
tab dat 111
dat 222
dat 333
dat 444
dat 555
one dat 1
size dat 5
temp dat
2hund dat 200
I tried to run the program step by step. I managed to turn the table into:
tab dat 111
dat 222
dat 333
dat 444
dat 111
but I dont know what to do afterwards
This is a good start. A few issues:
At save you will write the moved value, but thereby lose the value that was sitting there.
temp is not used, but you would need it for saving the original value before it is overwritten, so that you can then read it again from temp and write that back to the first half of the list.
When branching back to the top, you need size to be reduced by 2, not by 1, because the load address has increased by one, and the distance to the target reduced with 2, not 1. You could solve this by changing the start of your program to:
lda size
add one # to compensate for the minus 2
sta size
right_el lda size
sub two
sta size
... and define two as 2.
You need a stop condition. When the reduced size is zero or less, the program should stop.
The code at inc is never executed. It should be.
You need two more self-modifying instructions. You currently have them for:
Reading from the left side of the list
Writing to the right side of the list
But you also need two for:
Reading from the right side of the list
Writing to the left side of the list
Some other remarks:
Your code mixes two variants of mnemonics: sto and sta. I would stick with one flavour.
Instead of bra save, you could just move that targeted code block right there, so no branching is needed.
I would use twohund as label instead of 2hund. It is common practice to not start identifiers with digits, and some simulators might even have a problem with it.
I would use loop as label instead of right_el as surely the loop will have to cover the complete swap -- from left to right and vice versa.
The following three instructions:
sta size
lda load
add size
Can be written with just two:
sta size
add load
Here is the resulting code -- I suffixed a few of your labels with "left" and "right" so I could add my own and make the distinction:
start LDA size
ADD one # to compensate for the minus 2
STA size
loop LDA size
SUB two
BRP continue # check the loop-stop condition
quit HLT
continue BRZ quit
STA size
ADD loadleft # add size in one go
STA loadright # manage the other dynamic opcode
SUB twohund
STA saveright
SUB size
STA saveleft # and another dynamic code.
loadright DAT
STA temp # first save the value that is targeted
loadleft LDA tab
saveright DAT
LDA temp # copy in the other direction
saveleft DAT
inc LDA loadleft
ADD one
STA loadleft # use consistent mnemnonic
BRA loop
tab DAT 111
DAT 222
DAT 333
DAT 444
DAT 555
one DAT 1
two DAT 2
size DAT 5
temp DAT
twohund DAT 200
<script src="https://cdn.jsdelivr.net/gh/trincot/lmc#v0.816/lmc.js"></script>
You can run the code right here.

writing generated numbers to a table in assembler

I am generating 5 random numbers and I want to write them down to a table. At the end, in the variable array, I have one digit, that is, the last one that was generated. How can I make sure that they are all, the previous figure is also saved?
_DATA SEGMENT
array dd 5 dup(?)
_DATA ENDS
_TEXT SEGMENT
main proc
; -- generating random numbers --
call GetTickCount
push eax
call nseed
mov ecx, 5
generateRandomNumbers:
push ecx
;--- generating random numbers from 0 to 9 ---
push 9
call nrandom
mov array, eax
pop ecx
loop generateRandomNumbers
; -- end --
push 0
call ExitProcess
main endp
_TEXT ENDS
My output:
&array = 2
I would like to receive
&array = 5, 1, 7, 6, 4

Branching when negative accumulator

I'm trying to create a loop that will print if a number given is odd or even (Par). How do I branch the loop when the accumulator value is -1?
START INP // int(input(""))
STA n // n =
LOOP LDA n //
BRZ END // while n !=0:
SUB En // n - 1
STA n // n =
INP // int(input(""))
ADD sum //
STA sum //
BRA LOOP //
END LDA sum
OUT
BRP PO
PO LDA sum
BRZ EXIT
LDA sum
SUB TO
STA sum
BRA PO
ODDE LDA O
OTC
LDA D
OTC
LDA D
OTC
LDA E
OTC
O DAT 79
D DAT 68
E DAT 69
HLT
EXIT BRP PAR
HLT
PAR LDA P
OTC
LDA A
OTC
LDA R
OTC
P DAT 80
A DAT 65
R DAT 82
HLT
TO DAT 2
n DAT 0
sum DAT 0
En DAT 1
Par DAT -1
How do I branch the loop when the accumulator value is -1?
The LMC defines mailboxes with values between 0 and 999. They cannot be negative. Even though you can subtract a bigger value from a smaller value, the accumulator's value is then undefined. According to Wikipedia:
SUBTRACT [...] the actions of the accumulator are not defined for subtract instructions that cause negative results - however, a negative flag will be set so that 7xx (BRZ) and 8xx (BRP) can be used properly.
So the only way to reliably detect a negative value is by using BRP: that branch instruction will jump to the provided target address unless a recent subtraction had set the negative flag.
Code review
There are the following issues in your code:
Par DAT -1: as stated above, you cannot store -1 in an LMC mailbox. Mailboxes can only store values between 000 and 999.
Par versus PAR: you have two labels that only differ in capitalisation. LMC implementations are usually not case sensitive, so this would make those two labels the same. Better use entirely different labels.
BRP PO: The label PO points to the very next instruction, so this means that code execution will always continue at that instruction, whether you branch or not. It makes this instruction useless.
O DAT 79: this line appears right after a set of instructions that ends with OTC. If ever that code is executed, it will run into this DAT line. That could lead to undefined behaviour. You don't want this to happen. So make sure that DAT mailboxes are shielded from code execution. Add a HLT before a block of DAT to avoid that they are ever executed as if they were code. You have a similar issue at P DAT 80.
BRZ EXIT: at the EXIT address, you have a BRP, but as you can only arrive there when the accumulator is zero, the negative flag will not be set, and so BRP will branch always. Note that BRP branches when the negative flag is not set.
ODDE: this label is never referenced, and that code block is never executed. You could consider changing the BRA -- that appears just before it -- to a BRP. Then the execution will fall through when the last subtraction led to a negative result (virtually -1 in your case, but the accumulator is undefined).
If you correct all these issues, you'll get to an implementation that will be very close to this working version

How do AVR Assembly BRNE delay loops work?

An online delay loop generator gives me this delay loop of runtime of 0.5s for a chip running at 16MHz.
The questions on my mind are:
Do the branches keep branching if the register becomes negative?
How exactly does one calculate the values that are loaded in the beginning?
ldi r18, 41
ldi r19, 150
ldi r20, 128
L1: dec r20
brne L1
dec r19
brne L1
dec r18
brne L1
To answer your questions exactly:
1: The DEC instruction doesn't know about 'signed' numbers, it just decrements an 8-bit register. The miracle of twos complement arithmetic makes this work at the wraparound (0x00 -> 0xFF, is the same bit pattern as 0 -> -1). The DEC instruction also sets the Z flag in the status register, which BRNE uses to determine if branching should happen.
2: You can see from the AVR manual that DEC is a single cycle instruction. BRNE is also a single cycle when not branching, and 2 cycles when branching. therefore to compute the time of your loop, you need to count the number of times each path will be taken.
Consider a single DEC/BRNE loop:
ldi r8 0
L1: dec r8
brne L1
This loop will execute exactly 256 times, which is 256 cycles of DEC, and 512 cycles of BRNE, for a total of 768 cycles. At 16MHz, that's 48us.
Wrapping that in an outer delay loop:
ldi r7 10
ldi r8 0
L1: dec r8
brne L1
dec r7
brne L1
You can see that the outer loop counter will decrement every time the inner loop counter hits 0. Thus in our example the outer loop DEC/BRNE will happen 10 times(for 768 cycles), and the inner loop will happen 10 x 256 times so the total time for this loop is 10 x 48us + 48us for 528us. Similarly for 3 nested loops.
From here, it's trivial to figure out how many times each loop should execute to achieve the desired delay. It's the largest number of iterations the outer loop can do less than the desired time, then taking that time out, do the same for the next nested loop, and so on until the inner most loop fills up the tiny amount left.
How exactly does one calculate the values that are loaded in the beginning?
Calculate total amount of cycles => 0.5s * 16000000 = 8000000
Know the total cycles of r20 and r19 loops (from zero to zero), AVR registers are 8 bit, so a full loop is 256 times (dec 0 = 255). dec is 1 cycle. brne is 2 cycles when condition (branch) happens, 1 cycle when not.
So the most inner loop:
L1: dec r20
brne L1
Is from zero to zero (r20=0): 255 * (1+2) + 1 * (1+1) = 767 cycles (255 times the branch is taken, 1 time it goes through).
The second wrapping loop working with r19 is then: 255 * (767+1+2) + 1 * (767+1+1) = 197119 cycles
The single r18 loop when branch is taken is then 197119+1+2 = 197122 cycles. (197121 when branch is not taken = final exit of delay loop, I will avoid this -1 by a trick in next step).
Now this is almost enough to calculate initial r18, let's adjust the total cycles first by the O(1) code, that's three times ldi instruction, which takes 1 cycle: total2 = 8000000 - (1+1+1) + 1 = 7999998 ... wait, what is the last +1 there? That's fake additional cycle to delay, to make the final r18 loop pretend it costs same as non-final, i.e. 197122 cycles.
And that's it, the initial r18 must be enough to wait at least 7999998 cycles: r18 = (7999998 + 197122 - 1) div 197122 = 41. The " + 197122 - 1" part will make sure the abundant cycles fits constraint: 0 <= abundant_cycles < 197122 (remainder by 197122 division).
41 * 197122 = 8082002 ... this is too much, but now we can shave the extra cycles down by setting up also r19 and r20 to particular values, to fine-tuned the delay. So how much is to be shaved off? 8082002 - 7999998 = 82004 cycles.
The single r19 loop takes 770 cycles when branching and 769 when exiting, so again let's avoid the 769 by adjusting 82004 to only 82003 to be shaved off. 82003 div 770 = 106: 106 r19 loops can be skipped, r19 = 256 - 106 = 150. Now this will shave 81620 cycles, so 82003 - 81620 = 383 cycles more to be shaved off.
The single r20 loop takes 3 cycles when branching and 2 when exiting. Again I will take into account the exiting loop being only 2 cycles -> 383 => 382 to shave off. And 382 div 3 = 127, remainder 1. r20 = 256 - 127 = 129 and do one less to shave additional 3 cycles (to cover that remainder) = 128. Then 2 cycles (3-1) wait is missing to make it a full 8mil.
So:
ldi r18, 41
ldi r19, 150
ldi r20, 128
L1: dec r20
brne L1
dec r19
brne L1
dec r18
brne L1
According to my calculations should wait exactly 8000000-2 cycles (if not interrupted by something else).
Let's try to verify:
Initial r20: 1273 + 12 = 383 cycles
Initial r19: 1*(383+1+2) + 148*(767+1+2) + 1*(767+1+1) = 115115 cycles
(that's initial r20 incomplete cycle one time, then 149 times full time r20 cycle with the final one being -1 due to exiting brne)
The r18 total: 1*(115115+1+2) + 39*(197119+1+2) + 1*(197119+1+1) = 7999997 cycles.
And the three ldi are +3 cycles = 7999997+3 = 8000000.
And the missing 2 cycles are nowhere to be seen, so I made somewhere a mistake.
As you can see, the math behind is reasonably simple, but very mundane to do by hand, and prone to mistakes...
Ah, I think I know where I did the mistake. When I'm shaving off the abundant cycles, the termination loop is not involved (that's part of the actual delay process), so I shouldn't have adjusted the to_shave_off cycles by -1. Then After r19 = 106 I would have still to shave off 384 cycles, and that's exactly 384/3 = 128 loops to shave off from r20 = 256-128 = 128. No remainder, no missing cycle, perfect 8mil.
If you have trouble to follow this reverse calculation, try it other way, imagine 2 bit registers (0..3 values only), and do on paper similar loop with r18=r19=r20=2, and count the cycles manually to see how it is evolving. .. i.e. 3x ldi = +3, dec r20,brne,dec r20,brne(skip) = +5 cycles, dec r19, brne = +3, ... etc.
Edit: and this was explained before by Jester in his links. And I'm too lazy to clean this up down to some simple formula to create your own online calculator.

Marie simulator looping when not meant to after storing inputs

I have written this very basic Marie code for multiplying two numbers, X and Y. I built it without the first 6 lines and would just assign X and Y decimals to test the program but realized I need to allow the user to input the numbers. Now when I step through this or run it it just asks for input, stores X, asks for input, stores Y and then goes back to asking for Input, ie. the input for X. And it does this infinitely..... what?
Multiply_Subroutine, Dec 0
Input
Store X
Input
Store Y
multiply, Dec 0
Load Y
Skipcond 800
Jump end
Load temp
Add X
Store temp
Load Y
Subt One
Store Y
Skipcond 400
Jump multiply
Load temp
Store X
Output X
end, Halt
X, Dec 0
temp, Dec 0
Y, Dec 0
Null, Dec 0
One, Dec 1
It is because your multiply subroutine line name/variable has the operand 0 when it is line 8, it needs to be Dec 8 for it to work :)

Resources