Start outer incrementation value with 0 in the inner cicle - assembly - loops

I want to copy a 5x5 matrix of bits to a peripherical. The problem I´m having is that I can´t start the column cicle with the line incrementation variable with 0. In a high-level it would be like this (very simple):
for (line=0;line<4;line++)
for (column=0;column<4;column+++)
R2- line
R3- column
line_cicle:
CMP R2, 4
JZ end
ADD R2,1
column_cicle:
; do stuff that is not depend of the end of a line
CMP R3, 4
JZ line_cicle
; do stuff that is depend of the end of a line
ADD R3, 1
JMP column_cicle
That ADD R2,1 is what is messing up, but where do I put it so that it doesn´t start with 1?

I don't really understand what you're saying/doing with your proposed assembly implementation. Why are you initializing the register to 1 when your loop is supposed to start at 0?
But a for loop nested in another for loop is a relatively simple thing to write, so let's start over and just take things one step at a time, starting from the high-level C code:
for (line=0;line<4;line++)
for (column=0;column<4;column++)
Here is the first (outer) for loop:
xor eax, eax ; line = 0
.LineLoop:
; Do something with line (EAX).
; ...
inc eax ; ++line
cmp eax, 4
jb .LineLoop ; keep looping if line < 4
; We are now finished with the loop.
Now, of course, a compiler wouldn't generate this code. This is a very small loop—it only goes around 4 times—so the overhead of the loop is probably going to be substantial compared to the code that gets executed inside, on each iteration. So a compiler would actually unroll the loop 4 times, producing code that is not only faster but more readable. However, I digress…we were writing loops. :-)
We have the outer loop, and we need the inner loop. Of course, the inner loop is basically the same thing as the outer loop, just with a different variable. Here is the inner loop:
xor edx, edx ; column = 0
.ColumnLoop:
; Do something with column (EDX).
; ...
inc edx ; ++column
cmp edx, 4
jb .ColumnLoop ; keep looping if column < 4
; We are now finished with the loop.
Simple enough, right? I just changed the variable/register and the label name. The last task is to nest them. It turns out that is simple, too. The inner loop's code just gets stuck right in the outer loop's code, right there where I said Do something with line (EAX), since the inner loop is going to do something with line—it's going to loop through all of the columns associated with that line. It is another copy-paste job:
xor eax, eax ; line = 0
.LineLoop:
xor edx, edx ; column = 0
.ColumnLoop:
; Do something with line (EAX) and column (EDX).
; ...
inc edx ; ++column
cmp edx, 4
jb .ColumnLoop ; keep looping if column < 4
inc eax ; ++line
cmp eax, 4
jb .LineLoop ; keep looping if line < 4
; We are now finished with both loops.
Remember that you can choose different registers for your loop counters. I just arbitrarily chose EAX and EDX. If you are going to call a function inside the body of the loop that does something with the line and column, and that function expects its parameters to be passed in different registers, then you might as well use those registers as your loop counters.
Note that there is a slightly more optimal way to write this code that would eliminate the cmp instructions. Instead of starting from 0 and counting up (which requires us to do a comparison to see if we've reached the end yet), we can start from the end and count down. Then, we just take advantage of the fact that the dec instruction sets the zero flag (ZF) when the result is 0, branching directly on that flag, instead of having to do a comparison. The code is easier to understand than the explanation:
mov eax, 4 ; line = 4
.LineLoop:
mov edx, 4 ; column = 4
.ColumnLoop:
; Do something with line (EAX) and column (EDX).
; ...
dec edx ; --column
jnz .ColumnLoop ; keep looping if column > 0
dec eax ; --line
jnz .LineLoop ; keep looping if line > 0
; We are now finished with both loops.
The only issue with this is that you are looping backwards over the lines and columns. This is usually not a problem, though.

Related

Print from 1 to < user input in emu8086

I want to get a number (i.e 5) from the user and then print starting from 1 to < input (i.e 1 2 3 4)
But my code does not stop in "4" rather than the loop runs till "d"
I know that loop runs CX times
and as in 8086 MOVZX does not work that is why at first I moved AL to CL then zeroed the CH.
As someone mentioned that the problem is as I am moving AL to CX I'm not moving the value 4, I'm moving 34(ASCII value of 4) and so my loop runs 34 times.
Now how do I convert my user input value to decimal and move that to CX. Is there any way to take user input that will be stored in AL as decimal value?
org 100h
MOV AH, 1 ; Get user input
INT 21H
DEC AL ; Dec AL to satisfy the condition that it will print till < input
MOV BL,31H ; Initialize BL so that the output starts printing from 1
MOV CL,Al ; set counter register CX
MOV CH,00
Print:
MOV AH, 2 ; for output printing
MOV DL,0DH ; for output printing
INT 21H ; for output printing
MOV DL,0AH ; for output printing
INT 21H ; for output printing
MOV AH,2
MOV DL,BL ; print what is in BL
INT 21H
INC BL ; then increment BL
LOOP Print ; supposed to run the loop on Print what is the value in CL times
hlt
MOV AH, 1 ; Get user input
INT 21H
If you input 5 then the AL register will hold the number 35h which is the ASCII code of that key. You clearly want what that key represents which is 5. You need to subtract 30h (48).
mov ah, 01h ; DOS.GetKey
int 21h
sub al, '0'
dec al
mov cl, al
mov ch, 0
The rest of the program is fine for printing starting from 1 to < input.
Now how do I convert my user input value to decimal and move that to CX.
You've fallen into the trap of forgetting that loop conditions other than }while(--cx) are possible, using instructions other than loop.
loop is just a peephole optimization for dec cx / jnz (without affecting FLAGS). Only use it when that's actually the most efficient way to loop. (Or just never use it at all, because you need to understand conditional branches anyway so omitting loop is one fewer instruction to learn about / remember. Also, on most modern x86 CPUs, loop is much slower than dec/jnz. It's good if tuning for real 8086, or optimizing for code-size over speed, though. But only necessary as an optimization.
The easiest and most logically clear way to write this loop is:
MOV AH, 1 ; read a char from stdin into AL
INT 21H
mov cl, al ; ending character
mov bl, '1' ; b = current character, starting with '1'
.top: ; do {
... print CR/LF (not shown)
mov dl, bl
int 21h ; With AH=2 from printing CR/LF
inc bl ; b++
cmp bl, cl
jbe .top ; }while(b <= end_char);
Notice that I increment after printing. If you increment before printing, you'd use jb for }while(b < end_char).
On a real 8086, where loop is efficient, this does have more instructions and more code bytes inside the loop, and thus could be slower (if we consider a case where loop overhead matters, not with 3x slow int 21h system calls inside the loop).
But that trades off against smaller total code size (from the trivial loop setup). So it's a tradeoff between static code size vs. dynamic instruction count (and amount of code bytes that need to be fetched, which was the real issue on 8086).

How can I find the last 0 on an array in nasm?

I'm writing code to find the last 0 on an array.
Basically I need to move a new value on the "top" of each array, if it has only zeros it puts it at the end and if it finds other value it puts it on the last 0 (I'm treating my arrays as piles).
So far my subroutine works fine for the most part but sometimes it rewrites a value that I don't want (instead of getting the first value different from 0 it takes the next one). Here's the code I've been using to get the "top" of the array.
TOP:
xor ecx,ecx
xor ebx,ebx
TOP_FOR:
mov bx,word[eax+ecx*2] ;eax has the pointer of the array
cmp ecx,n ;n is the array's length
je END_TOP
inc ecx
cmp bx,0
je TOP_FOR
;here i get the direction of the first value different
END_TOP: ;from 0 but in my code i need the last 0, so
dec ecx ;i decrease ecx (result of this subrutine)
ret
For example,
If I put an array with 0,2 I expect ecx = 0, but with that input actually get 1.
With the array 1,2 I get 0 (which is what I want)
with the array 0,0 I get 1 (what I want, again)
Edit: tried starting the loop on n-1 and it's giving me even weirder results.
TOP:
xor ecx,ecx
;xor ebx,ebx
mov ecx,n-1
TOP_FOR:
;mov bx,word[eax+ecx*2]
cmp word[eax+ecx*2],0
je FIN_TOPE
dec ecx
cmp ecx,0
jne TOP_FOR
END_TOP:
ret
Your logic is totally backwards. Your cmp/je loop condition leaves the loop when you find the first non-zero. (And you've already incremented ECX after loading, but before checking it).
So after your loop, ECX = index of the element after the first non-zero element.
You at least 2 options:
remember the last-seen 0 in another register, and use it at the end of the loop
loop backwards, starting with ECX = n-1, and exit the loop on the first zero. (Or on dec ecx producing 0.)
One of these is obviously more efficient and easier than the other. :P
I'll leave it up to you to solve the off-by-1 problems, but probably you want to have the ecx < n or ecx >= 0 check at the bottom of the loop, e.g. dec ecx / jge TOP_FOR. i.e. a do{}while(--i) loop.
Also, normally EBX is a call-preserved register. You don't need to use it at all, though. cmp word [eax + ecx*2], 0 works fine.
Also in your current code, you read 2 bytes past the end of the array. potentially faulting if it was at the end of a page. (You don't use it, though, so it's not a correctness problem other than that.) You use ECX as an index before checking if it's too large! That problem goes away if you just use a memory, immediate cmp.
Also, normally a pointer-increment is more efficient. After the loop you can subtract and right-shift to get an index.

What is the correct way to loop if I use ECX in loop (Assembly)

I am currently learning assembly language, and I have a program which outputs "Hello World!" :
section .text
global _start
_start:
mov ebx, 1
mov ecx, string
mov edx, string_len
mov eax, 4
int 0x80
mov eax, 1
int 0x80
section .data
string db "Hello World!", 10, 0
string_len equ $ - string
I understand how this code works. But, now, I want to display 10 times the line. The code which I saw on the internet to loop is the following:
mov ecx, 5
start_loop:
; the code here would be executed 5 times
loop start_loop
Problem : I tried to implement the loop on my code but it outputs an infinite loop. I also noticed that the loop needs ECX and the write function also needs ECX. What is the correct way to display 10 times my "Hello World!" ?
This is my current code (which produces infinite loop):
section .text
global _start
_start:
mov ecx, 10
myloop:
mov ebx, 1 ;file descriptor
mov ecx, string
mov edx, string_len
mov eax, 4 ; write func
int 0x80
loop myloop
mov eax, 1 ;exit
int 0x80
section .data
string db "Hello World!", 10, 0
string_len equ $ - string
Thank you very much
loop uses the ecx register. This is easy the remember because the c stands for counter. You however overwrite the ecx register so that will never work!
The easiest fix is to use a different register for your loop counter, and avoid the loop instruction.
mov edi,10 ;registers are general purpose
loop:
..... do loop stuff as above
;push edi ;no need save the register
int 0x80
.... ;unless you yourself are changing edi
int 0x80
;pop edi ;restore the register. Remember to always match push/pop pairs.
sub edi,1 ;sub sometimes faster than dec and never slower
jnz loop
There is no right or wrong way to loop.
(Unless you're looking for every last cycle, which you are not, because you've got a system call inside the loop.)
The disadvantage of loop is that it's slower than the equivalent sub ecx,1 ; jnz start_of_loop.
The advantage of loop is that it uses less instruction bytes. Think of it as a code-size optimization you can use if ECX happens to be a convenient register for looping, but at the cost of speed on some CPUs.
Note that the use of dec reg + jcc label is discouraged for some CPUs (Silvermont / Goldmont being still relevant, Pentium 4 not). Because dec only alters part of the flags register it can require an extra merging uop. Mainstream Intel and AMD rename parts of EFLAGS separately so there's no penalty for dec / jnz (because jnz only reads one of the flags written by dec), and can even micro-fuse into a dec-and-branch uop on Sandybridge-family. (But so can sub). sub is never worse, except code-size, so you may want to use sub reg,1 for the forseeable future.
A system call using int 80h does not alter any registers other than eax, so as long as you remember not to mess with edi you don't need the push/pop pair. Pick a register you don't use inside the loop, or you could just use a stack location as your loop counter directly instead of push/pop of a register.

Assembly language x86 IRVINE infinite loops

I'm new to Assembly language and I need help with an error I keep experiencing, my task in class is to do what I did below, everything I did is correct and how the professor wants it but I can't stop the program from infinite looping, I have the correct answer which is 14 but how do I stop the loop from being infinite without using special commands like ret. How do I stop it?
;Declare an array of words
;Write a loop that adds all the elements of the array located in even places
;Example 3,7,2,8,9
;3+2+9=14
INCLUDE Irvine32.inc
.data
val1 WORD 3,7,2,8,9
.code
main PROC
mov eax, 0
L1:
mov ecx, (LENGTHOF val1)*(TYPE val1+2)-(TYPE val1+4)
mov eax, ecx
call writeDec
loop L1
exit
main ENDP
END main
At the bottom of your loop, you have this instruction:
loop L1
which means "go back to L1."
Loop L1 is a conditional LOOP that is based on the value in ECX. The
real issue is the problem with the value in ECX. LOOP will first
decrement ECX by 1 and compare the new value in ECX with zero. If it
isn't zero it goes to the label (L1). If it is zero it falls through.
Look carefully at where you set the value of ecx. After LOOP decrements ecx by 1 and execution goes to L1, what happens?

Regarding Assembly Language Loops

Can anyone explain what this code does? I kind of understand it, but I don't quite understand what happens when the code label appears below "loop N-Not-1". I'm not sure if I understand loops correctly. I think of them as do-while loops in C++. In this case, wouldn't the loop for N-is-1 continue indefinitely? I thought this was a if-else statement and not a loop?
Write a piece of code that computes the function below:
if (N = 1) then Y = -X
else
Y=X
Assume that the value of X is in the eax register. Also assume that the value of N is in the
ebx register. The computed value of Y need to be placed in the eax register.
Hint 1: Use a loop instruction in your code.
Hint 2: This problem can be solved using less than five instructions.
; eax = X, ebx = N
; Write your code below
mov ecx, ebx
loop N-not-1
N-is-1: neg eax
N-not-1: ; Y = eax
loop instruction operates on the value of ECX. It decreases ECX first and checks whether it is zero. If it is not zero, then it jumps to the specified address. If it is zero then break.
mov ecx, ebx ; this instruction moves the value of N to ecx
loop N-not-1 ; if N is 1 then, on decrementing it becomes 0 and the loop breaks.
N-is-1: neg eax ; if N is 1, eax gets negated as the loop breaks
N-not-1: ; Y = eax // if N is not 1, eax remains unchanged
First of all, this code is very badly presented. I am copying it here without the clutter:
mov ecx, ebx
loop N-not-1
neg eax
N-not-1:
This code is a hack. It does not actually loop. It just makes use of the fact that the loop instruction will do 3 things : decrement ecx, check if it is zero, and jump all in one instruction. It is equivalent to the following:
dec ebx
cmp ebx, 0
jnz N-not-1
neg eax
N-not-1:
loop instruction is like the following C code:
for (; ecx != 0; ecx--) {
// instructions
}
and your code use loop instruction for checking ebx(N) against zero:
ebx = N;
ecx = ebx;
for (; ecx != 0; ecx--) {
// if ecx=1 (N = 1) we enter the loop
eax = -eax
}
Y = eax
I used C like code, but in assembly there is no block and everything is controlled with labels.

Resources