Related
I have a function that receives a number from 0 to 10 as an input in R0. Then I need to place the multiplication table from 1 to 10 into an array in the data segment and place the address of the result array in R1.
I have a loop to make the arithmetic operation and have the array setup however I have no idea how to place the values in the array.
Mi original idea is each time the loop runs it calculates an iteration and it stored in the array and so on.
myArray db 1000 dup (0)
.code
MOV R0,#8 ;user input
MOV R11, #9 ;reference to stop loop when it reaches 10th iteration
loop
ADD R10, R10, #1 ;functions as counter
ADD R1,R0,R1 ;add the input number to itserlf and stores it in r1
CMP R11,R10 ;substracts counter from 9
BMI finish ;if negative flag is set it ends the loop
B loop ;if negative flag is zero it continues
finish
end
Any help is much appreciated
Your code is on the right track but it needs some fixing.
To specifically answer your question about load and store, you need to reserve space in memory, make a pointer, and load and store to the location the pointer is pointing to. The pointer can be specified by a register, like R0.
Here is a play list of YT vids that covers all the things you need to make a loop (from memory allocation, to doing load store and looping). At the very least you can watch the code sections, load-store instructions, and looping and branch instructions videos.
Good luck!
I have just started to learn assembly language at school, and as an exercise I have to make a program that calculate the sum of the first n integers (1+2+3+4+5+...+n).
I managed to build this program but during the comparison (line.9) I only compare the even numbers in register R1, so I would have to do another comparison for the odd numbers in R0.
MOV R0,#1 ; I put a register at 1 to start the sequence
INP R2,2 ; I ask the user up to what number the program should calculate, and I put its response in the R2 register
B myloop ; I define a loop
myloop:
ADD R1,R0,#1 ; I calculate n+1 and put it in register 1
ADD R3,R1,R0 ; I add R0 and R1, and I put the result in the register R3
ADD R0,R1,#1 ; I calculate n+2 and I put in the register R0, and so on...
ADD R4,R4,R3 ; R4 is the total result of all additions between R0 and R1
CMP R1,R2 ; I compare R1 and the maximum number to calculate
BNE myloop ; I only exit the loop if R1 and R2 are equal
STR R4,100 ; I store the final result in memory 100
OUT R4,4 ; I output the final result of the sequence
HALT ; I stop the execution of the program
I've tried several methods but I can't manage to perform this double comparison... (a bit like an "elif" in python)
Basically I would like to add this piece of code to also compare odd numbers:
CMP R0,R2
BNE myloop
But adding this like this directly after comparing even numbers doesn't work no matter if I put "BNE" or not.
You're trying to do a conjunction, in context, something like this:
do {
...
} while ( odd != n && even != n );
...
Eventually, one of those counters should reach the value n and stop the loop. So, both tests must pass in order to continue the loop. However, if either test fails, then the loop should stop.
First, we'll convert this loop into the if-goto-label form of assembly (while still using the C language!):
loop1:
...
if ( odd != n && even != n ) goto loop1;
...
Next, let's break down the conjunction to get rid of it. The intent of the conjunction is that if the first component fails, to stop the loop, without even checking the second component. However, if the first component succeeds, then go on to check the second component. And if the second also succeeds, then, and only then return to the top of the loop (knowing both have succeeded), and otherwise fall off the bottom. Either way, whether the first component fails or the second component fails, the loop stops.
This intent is fairly easy to accomplish in if-goto-label:
loop1:
...
if ( odd == n ) goto endLoop1;
if ( even != n ) goto loop1;
endLoop1:
...
Can you figure out how to follow this logic in assembly?
Another analysis might look like this:
loop1:
...
if ( odd == n || even == n ) goto endLoop1;
goto loop1;
endLoop1:
...
This is the same logic, but stated as how to exit the loop rather than how to continue the loop. The condition is inverted (De Morgan) but the intended target of the if-goto statement is also changed — it is effectively doubly-negated, so holds the same.
From that we would strive to remove the disjunction, again by making two statements instead of one with disjunction, also relatively straightforward using if-goto:
loop1:
...
if ( odd == n ) goto endLoop1;
if ( even == n ) goto endLoop1;
goto loop1;
endLoop1:
...
And with an optimization sometimes known as branch over unconditional branch (the unconditional branch is goto loop1;), we perform a pattern substitution, namely: (1) reversing the condition of the conditional branch, (2) changing the target of the conditional branch to the target of the unconditional branch, and (3) removing the unconditional branch.
loop1:
...
if ( odd == n ) goto endLoop1;
if ( even != n ) goto loop1;
endLoop1:
...
In summary, one takeaway is to understand how powerful the primitive if-goto is, and that it can be composed into conditionals of any complexity.
Another takeaway is that logic can be transformed by pattern matching and substitution into something logically equivalent but more desirable for some purpose like writing assembly! Here we work toward increasing use of simple if-goto's and lessor use of compound conditions.
Also, as #vorrade says, programs generally should not make assumptions about register values — so suggest to load 0 into the registers that need it at the beginning of the program to ensure their initialization. We generally don't clear registers after their use but rather set them before use. So, in another larger program, your code might run with other values from some other code left over in those registers.
Also, I answer the question posed, which is about compound conditionals, and explain how those work in some detail; though as stated elsewhere, there's no need to separate even and odd numbers in order to sum them, and, there's also a single formula that can compute the sum of numbers without iteration (though does require multiplication which may not be directly available and so would require a more bounded iteration..).
First of all your code assumes that R4 is 0 at the beginning.
This might not be true.
Your program becomes simpler and easier to understand if you add each number in a smaller loop, like this:
INP R2,2 ; I ask the user up to what number the program should calculate, and I put its response in the R2 register
MOV R0,#0 ; I put a register at 0 to start the sequence
MOV R4,#0 ; I put a register at 0 to start the sum
B testdone ; Jump straight to test if done
myloop:
ADD R0,R0,#1 ; I calculate n+1 and keep it in register 0
ADD R4,R4,R0 ; I add R4 and R0, and I put the result in the register R4
testdone:
CMP R0,R2 ; I compare R0 and the maximum number to calculate
BNE myloop ; I only exit the loop if R0 and R2 are equal
STR R4,100 ; I store the final result in memory 100
OUT R4,4 ; I output the final result of the sequence
HALT ; I stop the execution of the program
You only need 3 registers: R0 for current n, R2 for limit and R4 for the sum.
However, if you really have to add the even and odd numbers separately, you could do this way:
INP R2,2 ; I ask the user up to what number the program should calculate, and I put its response in the R2 register
MOV R0,#0 ; I put a register at 0 to start the sequence
MOV R4,#0 ; I put a register at 0 to start the sum
B testdone ; Jump straight to test if done
myloop:
ADD R0,R0,#1 ; I calculate n+1 (odd numbers), still register 0
ADD R4,R4,R0 ; I add R4 and R0, keep the result in the register R4
CMP R0,R2 ; I compare R0 and the maximum number to calculate
BEQ done ; I only exit the loop if R0 and R2 are equal
ADD R0,R0,#1 ; I calculate n+1 (even numbers) and keep it in register 0
ADD R4,R4,R0 ; I add R4 and R0, and I put the result in the register R4
testdone:
CMP R0,R2 ; I compare R0 and the maximum number to calculate
BNE myloop ; I only exit the loop if R0 and R2 are equal
done:
STR R4,100 ; I store the final result in memory 100
OUT R4,4 ; I output the final result of the sequence
HALT ; I stop the execution of the program
First of all I would like to thank you very much #vorrade , #Erik Eidt and #Peter Cordes, I read your comments and advice very carefully, and they are very useful to me :)
But in fact following the post of my question I continued to seek by myself a solution to my problem, and I came to develop this code which works perfectly!
// Date : 31/01/2022 //
// Description : A program that calculate the sum of the first n integers (1+2+3+4+5+...+n) //
MOV R0,#1 // I put a register at 1 to start the sequence of odd number
INP R2,2 // I ask the user up to what number the program should calculate, and I put its response in the R2 register
B myloop // I define a main loop
myloop:
ADD R1,R0,#1 // I calculate n+1 (even) and I put in the register R1, and so on...
ADD R3,R1,R0 // I add R0 and R1, and I put the result in the register R3
ADD R4,R4,R3 // R4 is the total result of all additions between R0 and R1, which is the register that temporarily stores the calculated results in order to increment them later in register R4
ADD R0,R1,#1 // I calculate the next odd number to add to the sequence
B test1 // The program goes to the first comparison loop
test1:
CMP R0,R2 // I compare the odd number which is in the current addition with the requested maximum, this comparison can only be true if the maximum is also odd.
BNE test2 // If the comparison is not equal, then I move on to the next test which does exactly the same thing but for even numbers this time.
ADD R4,R4,R2 // If the comparison is equal, then I add a step to the final result because my main loop does the additions 2 by 2.
B final // The program goes to the final loop
test2:
CMP R1,R2 // I compare the even number which is in the current addition with the requested maximum, this comparison can only be true if the maximum is also even.
BNE myloop // If the comparison is not equal, then the program returns to the main loop because this means that all the comparisons (even or odd) have concluded that the maximum has not yet been reached and that it is necessary to continue adding.
B final // The program goes to the final loop
final:
STR R4,100 // I store the final result in memory 100
OUT R4,4 // I output the final result of the sequence
HALT // I stop the execution of the program
I made some comments to explain my process!
I now realize that the decomposition into several loops was indeed the right solution to my problem, it allowed me to better realize the different steps that I had first written down on paper.
I'm trying to figure out a way to implement the Fibonacci sequence using a 68HC11 IDE that uses a Motorolla as11 assembler.
I've done it using 2-byte unsigned in little-endian format, now I'm attempting to change it using 4-byte variables, using big-endian
My pseudo-code (which is written in c):
RESULT = 1;
PREV = 1;
COUNT = N;
WHILE(COUNT > 2){
NEXT = RESULT + PREV;
PREV = RESULT;
RESULT = NEXT;
COUNT--;
}
I'll include some of my current assembly code. Please note that count is set to unsigned int at 1-byte, and prev, next, and result are unsigned ints at 2 bytes. N is unsigned, set to 10.
ORG $C000
LDD #1
STD RESULT
STD PREV
LDAA N
STAA COUNT
WHILE LDAA COUNT
CMPA #2
BLS ENDWHILE
LDD RESULT
ADDD PREV
STD NEXT
LDD RESULT
STD PREV
LDD NEXT
STD RESULT
DEC COUNT
BRA WHILE
ENDWHILE
DONE BRA DONE
END
The issue that I'm having is now altering this (other than the obvious variable changes/declarations) N will begin at 40 now, not 10. Would altering my pseudo-code to include pointers allow me to implement it 1 to 1 better with big-endian? Since this is in little-endian, I assume I have to alter some of the branches. Yes this is an assignment for class, I'm not looking for the code, just some guidance would be nice.
Thank you!
(Your problem description is a bit vague as to what your actual problem is, so I may be guessing a bit.)
BTW, 68HC11 is big-endian.
The 68HC11 has a 16-bit accumulator, so as soon as your result overflows this, you need to do math operations in pieces.
I suppose you mean that by changing N from 10 to 40 your fibonacci number becomes too big to be stored in a 16-bit variable.
The use or not of pointers is irrelevant to your problem as you can solve it both with or without them. For example, you can use a pointer to tell your routine where to store the result.
Depending on your maximum expected result, you need to adjust your routine. I will assume you won't need to go over 32-bit result (N=47 => 2971215073).
Here's a partially tested but unoptimized possibility (using ASM11 assembler):
STACKTOP equ $1FF
RESET_VECTOR equ $FFFE
org $100 ;RAM
result rmb 4
org $d000 ;ROM
;*******************************************************************************
; Purpose: Return the Nth fibonacci number in result
; Input : HX -> 32-bit result
; : A = Nth number to calculate
; Output : None
; Note(s):
GetFibonacci proc
push ;macro to save D, X, Y
;--- define & initialize local variables
des:4 ;allocate 4 bytes on stack
tmp## equ 5 ;5,Y: temp number
ldab #1
pshb
clrb
pshb:3
prev## equ 1 ;1,Y: previous number (initialized to 1)
psha
n## equ 0 ;0,Y: N
;---
tsy ;Y -> local variables
clra
clrb
std ,x
std prev##,y
ldd #1
std 2,x
std prev##+2,y
Loop## ldaa n##,y
cmpa #2
bls Done##
ldd 2,x
addd prev##+2,y
std tmp##+2,y
ldaa 1,x
adca prev##+1,y
staa tmp##+1,y
ldaa ,x
adca prev##,y
staa tmp##,y
ldd ,x
std prev##,y
ldd 2,x
std prev##+2,y
ldd tmp##,y
std ,x
ldd tmp##+2,y
std 2,x
dec n##,y
bra Loop##
Done## ins:9 ;de-allocate all locals from stack
pull ;macro to restore D, X, Y
rts
;*******************************************************************************
; Test code
;*******************************************************************************
Start proc
ldx #STACKTOP ;setup our stack
txs
ldx #result
ldaa #40 ;Nth fibonacci number to get
bsr GetFibonacci
bra * ;check 'result' for answer
org RESET_VECTOR
dw Start
I'm trying to copy array A into array N and then print the array (to test that it has worked) but all it outputs is -1
Here is my code:
ORG $1000
START: ; first instruction of program
clr.w d1
movea.w #A,a0
movea.w #N,a2
move.w #6,d2
for move.w (a0)+,(a2)+
DBRA d2,for
move.w #6,d2
loop
move.l (a2,D2),D1 ; get number from array at index D2
move.b #3,D0 ; display number in D1.L
trap #15
dbra d2,loop
SIMHALT ; halt simulator
A dc.w 2,2,3,4,5,6
N dc.l 6
END START ; last line of source
Why is -1 in the output only? If there is a better solution for this that would be very helpful
Since I don't have access to whatever assembler/simulator you're using, I can't actually test it, but here a few things (some of which are already noted in the comments):
dc.l declares a single long, you want ds.l (or similar) to allocate storage for 6 longs
dbra branches until the operand is equal to -1, so you'll probably want to turn
movw #loop_times, d0
loop
....
dbra d0, loop
into
movw #loop_times-1, d0
loop
....
dbra d0, loop
(this works as long as loop_times is > 0, otherwise you'll have to check the condition before entering the loop)
You display loop has a few problems: 1. On entry a2 points past the end of the N array. 2. Even fixing that, the way you're indexing it will cause problems. On the first entry you're trying to fetch a 4-byte long from address a2 + 6,then a long from a2 + 5...
What you want is to fetch longs from address a2 + 0, a2 + 4 .... One way of doing that:
move.w #6-1, d2 ; note the -1
movea.l #N, a2
loop
move.l (a2)+,D1 ; get next number from array
; use d1 here
dbra d2,loop
As already pointed out, your new array is only 4 bytes in size, you should change
dc.l 6 to ds.w 6
and also you work on 7 elements, since DBRA counts down to -1.
Second, and thats why you get -1 everywhere, you use A2 as pointer to the new array, but you do not reset it to point at the first word in new array. Since you increased it by one word per element during the copy, after the for loop has completed, A2 points to the first word after the array.
Your simulator outputting more than one number with your display loop indicates that your simulator does not emulate an MC68000, a real MC68000 would take a trap at "MOVE.L (A2,D2),D1" as soon as the sum of A2+D2 is odd - the 68000 does not allow W/L sized accesses to odd addresses (MC68020 and higher do).
A cleaned MC68000 compatible code could look like this:
lea A,a0
lea N,a2
moveq #5,d2
for move.w (a0)+,(a2)+
dbra d2,for
lea N,a2
moveq #5,d2
loop
move.w (a2)+,D1 ; get number (16 bits only)
ext.l d1 ; make the number 32 bits
moveq #3,D0 ; display number in D1.L
trap #15
dbra d2,loop
It probably contains some instructions you haven't encountered yet.
Typical strlen() traverse from first character till it finds \0.
This requires you to traverse each and every character.
In algorithm sense, its O(N).
Is there any faster way to do this where input is vaguely defined.
Like: length would be less than 50, or length would be around 200 characters.
I thought of lookup blocks and all but didn't get any optimization.
Sure. Keep track of the length while you're writing to the string.
Actually, glibc's implementation of strlen is an interesting example of the vectorization approach. It is peculiar in that it doesn't use vector instructions, but finds a way to use only ordinary instructions on 32 or 64 bits words from the buffer.
Obviously, if your string has a known minimum length, you can begin your search at that position.
Beyond that, there's not really anything you can do; if you try to do something clever and find a \0 byte, you still need to check every byte between the start of the string and that point to make sure there was no earlier \0.
That's not to say that strlen can't be optimized. It can be pipelined, and it can be made to process word-size or vector chunks with each comparison. On most architectures, some combination of these and other approaches will yield a substantial constant-factor speedup over a naive byte-comparison loop. Of course, on most mature platforms, the system strlen is already implemented using these techniques.
Jack,
strlen works by looking for the ending '\0', here's an implementation taken from OpenBSD:
size_t
strlen(const char *str)
{
const char *s;
for (s = str; *s; ++s)
;
return (s - str);
}
Now, consider that you know the length is about 200 characters, as you said. Say you start at 200 and loop up and down for a '\0'. You've found one at 204, what does it mean? That the string is 204 chars long? NO! It could end before that with another '\0' and all you did was look out of bounds.
Get a Core i7 processor.
Core i7 comes with the SSE 4.2 instruction set. Intel added four additional vector instructions to speed up strlen and related search tasks.
Here are some interesting thoughts about the new instructions:
http://smallcode.weblogs.us/oldblog/2007/11/
The short answer: no.
The longer answer: do you really think that if there were a faster way to check string length for barebones C strings, something as commonly used as the C string library wouldn't have already incorporated it?
Without some kind of additional knowledge about a string, you have to check each character. If you're willing to maintain that additional information, you could create a struct that stores the length as a field in the struct (in addition to the actual character array/pointer for the string), in which case you could then make the length lookup constant time, but would have to update that field each time you modified the string.
You can try to use vectorization. Not sure if compiler will be able perform it, but I did it manually (using intrinsics). But it could help you only for long strings.
Use stl strings, it's more safe and std::string class contains its length.
Here I attached the asm code from glibc 2.29. I removed the snippet for ARM cpus. I tested it, it is really fast, beyond my expectation. It merely do alignment then 4 bytes comparison.
ENTRY(strlen)
bic r1, r0, $3 # addr of word containing first byte
ldr r2, [r1], $4 # get the first word
ands r3, r0, $3 # how many bytes are duff?
rsb r0, r3, $0 # get - that number into counter.
beq Laligned # skip into main check routine if no more
orr r2, r2, $0x000000ff # set this byte to non-zero
subs r3, r3, $1 # any more to do?
orrgt r2, r2, $0x0000ff00 # if so, set this byte
subs r3, r3, $1 # more?
orrgt r2, r2, $0x00ff0000 # then set.
Laligned: # here, we have a word in r2. Does it
tst r2, $0x000000ff # contain any zeroes?
tstne r2, $0x0000ff00 #
tstne r2, $0x00ff0000 #
tstne r2, $0xff000000 #
addne r0, r0, $4 # if not, the string is 4 bytes longer
ldrne r2, [r1], $4 # and we continue to the next word
bne Laligned #
Llastword: # drop through to here once we find a
tst r2, $0x000000ff # word that has a zero byte in it
addne r0, r0, $1 #
tstne r2, $0x0000ff00 # and add up to 3 bytes on to it
addne r0, r0, $1 #
tstne r2, $0x00ff0000 # (if first three all non-zero, 4th
addne r0, r0, $1 # must be zero)
DO_RET(lr)
END(strlen)
If you control the allocation of the string, you could make sure there is not just one terminating \0 byte, but several in a row depending on the maximum size of vector instructions for your platform. Then you could write the same O(n) algorithm using X bytes at a time comparing for 0, making strlen amortized O(n/X). Note that the amount of extra \0 bytes would not be equal to the amount of bytes on which your vector instructions operate (X), but rather 2*X - 1 since an aligned region should be filled with zeroes.
You would need to iterate over a couple of bytes normally in the beginning though, until you reach an address that is aligned to a boundary of X bytes.
The use case for this is kind of non-existent though: the amount of extra bytes you need to allocate would easily be more than simply storing a simple 4 or 8 byte integer containing the size directly. Even if it is important to you for some reason that this string can be passed solely as a pointer, without passing its size as well I think storing the size as the first Y bytes during allocation might be the fastest. But this is already far from the strlen optimization you're asking about.
Clarification:
the_size | the string ...
^
the pointer to the string
The glibc implementation is way cooler.