This builds successfully and there is nothing wrong with typing integers. However, I can't see the outcome of gcd and the debugger runs without anything. Is it because of the infinite loop? I am totally lost in here. Could anyone figure out what I am missing here? Please help me what is wrong with this?
INCLUDE Irvine32.inc
.data
strA BYTE "Enter an integer A: ",0
strB BYTE "Enter an integer B: ",0
temp DWORD ?
finalStr BYTE "GCD of the two integers is: ",0
.code
main PROC
call Clrscr
mainLoop:
mov edx,OFFSET strA
call WriteString
call ReadInt
mov temp, eax
call Crlf
mov edx, OFFSET strB
call WriteString
call ReadInt
mov ebx, eax
mov eax, temp
call Crlf
call GCD
mov edx, OFFSET finalStr
call WriteString
call WriteInt
call WaitMsg
jmp mainLoop
main ENDP
abs PROC
cmp eax, 0 ; see if we have a negative number
jge done
neg eax
done:
ret
abs ENDP
gcd PROC
call abs ;takes absolute value of both registers
mov temp, eax
mov eax, ebx
call abs
mov ebx, eax
mov eax, temp
cmp eax, ebx ; making sure we divide the bigger number by the smaller
jz DONE ; if numbers are equal, GCD is eax either way
jc SWITCH ;swaps if ebx is larger then eax
mov edx, 0
SWITCH: ;swaps values so eax is larger then ebx
mov temp, eax
mov eax, ebx
mov ebx, temp
mov edx, 0
jmp L1
L1: ;divides until remainder is 0, then eax is GCD
div ebx
cmp edx, 0
jz DONE
mov eax, edx
jmp L1
DONE:
gcd ENDP
END main
Your GCD function is missing a return instruction:
DONE: <-- There should be a RET after the DONE label
gcd ENDP
I'll tell you one thing wrong with that code straight up (there may be other issues, that's just the one that immediately popped out):
jc SWITCH ;swaps if ebx is larger then eax
mov edx, 0
SWITCH: ;swaps values so eax is larger then ebx
mov temp, eax
mov eax, ebx
mov ebx, temp
mov edx, 0
jmp L1
L1: ;divides until remainder is 0, then eax is GCD
This will switch the registers no matter what since regardless of the state of the carry flag, you run the code at SWITCH. Either you explicitly jump to there, or you fall through to there.
I suspect the jmp L1 (which is superfluous at its current position, for reasons identical to the jump if carry) should be immediately after the jc SWITCH so that the whole thing either swaps or doesn't swap.
In other words, something like:
jnc L1 ; skip swap unless ebx > eax.
SWITCH: push eax ; don't need temp at all, use stack.
push ebx
pop eax
pop ebx
L1:
mov edx, 0 ; carry on.
Related
I have elements loaded in stack and I need to move them to array. My code looks like this:
%include "asm_io.inc"
segment .data
array db 100 dup(0)
length db 0
segment .text
global _asm_main
extern getchar
_asm_main:
enter 0, 0
pusha
call getchar
char_loop:
mov ebx,10
sub eax, '0'
mul ebx
mov ebx, eax
call getchar
sub eax, '0'
add eax, ebx
push eax
inc BYTE[length]
call getchar
cmp eax, 10
je fill_array
cmp eax, 13
je fill_array
cmp eax, 32
je skip_spaces
jmp char_loop
skip_spaces:
call getchar
cmp eax, 32
je skip_spaces
jmp char_loop
fill_array:
mov ecx, [length]
mov ebx, array
l1:
pop eax
mov [ebx], eax ; should be al instead of eax
inc ebx
call print_int
call print_nl
loop l1
print_array:
mov ecx, [length]
mov ebx, array
l2:
mov eax, [ebx] ; should be al instead of eax
call print_int
call print_nl
inc ebx
loop l2
_asm_end:
call print_nl
popa
mov eax, 0
leave
ret
print_int in asm_io.asm is
print_int:
enter 0,0
pusha
pushf
push eax
push dword int_format
call _printf
pop ecx
pop ecx
popf
popa
leave
ret
Where int_format is int_format db "%i",0
Length and values in stack are correct, I had them printed but when I try to print array only last value is correct. Other values are random numbers. I tried combinations of registers of different sizes but it did not work. I think that error has to do something with size of registers or size of array.
Answer here:
As #xiver77 said in comments I was writing into array 4 bytes instead 1 byte. One element in array has 1 byte and I tried to write 4 bytes. That creates overflow of bites and change other elements in array. Instead mov [ebx], eax should be mov [ebx], al and mov eax, [ebx] for printing should be mov al [ebx].
I am trying to write a program that gets a number with one or two digits and write Hello! as many times as that number.
I used this posts to write my code:
NASM: The loop turns into an infinite loop
Check null character in assembly language
Multi-Digit Input from NASM I did not understand this :)
But my code only works for two digit numbers and the result for single digit numbers are wrong.
My code:
section .data
msg db 'Hello!',0xA
len equ $-msg
section .bss
n resb 2
section .text
global _start
_start:
;get n
mov edx, 2
mov ecx, n
mov ebx, 0
mov eax, 3
int 0x80
lea esi,[n] ;address of our text
call toint
;loop for print 'Hello!\n'
print_loop:
push ecx
mov edx, len
mov ecx, msg
mov ebx, 1
mov eax, 4
int 0x80
pop ecx
loop print_loop
mov eax, 1
int 0x80
toint:
push eax
push ebx
xor ebx, ebx
next_digit:
movzx eax, byte[esi]
sub al , '0'
imul ebx, 10
add ebx, eax
inc esi
cmp byte [esi] , 0x0 ;check next character is null or not
jne next_digit
; end_loop:
mov ecx, ebx
pop eax
pop ebx
ret
The sys_read call returns in EAX the count of characters that were sent to your inputbuffer. Because you allowed for an input of max. 2 characters, this count will be either 0, 1, or 2. You could use this info in your toint routine.
; IN (eax,esi) OUT (ecx)
toint:
mov ecx, eax ; Number of digits
jecxz done
push eax ; (1)
push ebx ; (2)
push esi ; (3)
xor ebx, ebx
next:
movzx eax, byte [esi]
sub al , '0'
imul ebx, 10
add ebx, eax
inc esi
dec ecx
jnz next
mov ecx, ebx
pop esi ; (3)
pop ebx ; (2)
pop eax ; (1)
done:
ret
Please notice that there's a reversed ordering to be observed when preserving/restoring registers on the stack! (Your code missed this...)
4 tips
a. Prefer the MOV variant to load an address. It's always a shorter instruction.
b. Guard yourself against an input of zero.
c. Don't use LOOP. It's a slow instruction.
d. Provide the exit code to terminate the program.
mov esi, n ; (a)
call toint ; -> ECX
jecxz Exit ; (b)
print_loop:
...
dec ecx ; (c)
jnz print_loop
Exit:
xor ebx, ebx ; (d)
mov eax, 1
int 0x80
I'm having trouble figuring out how to do the indexing in my loops. I know esi is used for indexing, so I'm attempting to use that...
scores DWORD MAXIMUMSCORES DUP(0)
optionPromptMsg byte "Type 1 to continue or -1 to exit: ", 0
scorePromptMsg byte "Enter your numeric score (0-100): ", 0
scoreErrorMsg byte "Score out of range (0-100)!", 0
optionErrorMsg byte "Only 0 or 1 allowed in option specification!", 0
resultMsg byte " scores have been entered.", 0
.code
main proc
mov esi, 0
L1:
mov edx, offset optionPromptMsg
call WriteString
call ReadInt
mov ebx, 1
cmp eax, ebx
je L2
mov ebx, -1 //99% sure my main is okay
cmp eax, ebx
je L3
mov ebx, -2
mov ecx, 2
cmp ebx, eax
ja L4
cmp eax, ecx
ja L4
L2: call NextScore
jmp L5
L4: mov edx, offset optionErrorMsg
call WriteString
call Crlf
jmp L5
L5:
loop L1
L3 : call WriteScore
exit
main ENDP
WriteScore PROC USES esi //Thought was somehow make esi global?
mov eax, lengthof scores //total number of items added to array
call writeInt
mov edx, offset resultMsg
call WriteString
mov esi,0
L1:
mov eax, scores[esi *4]
call writeInt //writes the numbers in the array
inc esi
loop L1
mov eax, 5000
call Delay
ret
WriteScore ENDP
NextScore PROC USES esi
mov edx, offset scorePromptMsg
call WriteString
call ReadInt
mov ebx, 0
mov ecx, 100
cmp ebx, eax
ja L1
cmp eax,ecx
ja L1
jmp L2
L1:
mov edx, offset scoreErrorMsg
call WriteString
call Crlf
L2:
mov scores[esi*4], eax //gets the next number and puts it in the array
inc esi
ret
NextScore ENDP
When I run it, and add 3 items to the array, it for whatever reason says the lengthof scores is 20, and then when it prints out the array, the numbers aren't even close to what I'm expecting, normally in the millions or just 0.
Any suggestions are much appreciated!
You have a couple issues. One is that you don't seem to understand what the USES directive on a procedure/function is for. If you use USES and list a register(s) then that tells the assembler to save the value of those registers on the stack, and restore them just before the function exits. This means that any change you make to that register in that function will not be seen by the function that called it.
The MASM manual says this about USES:
Syntax: USES reglist
Description:
An optional keyword used with PROC. Generates code to push the
value of registers that should be preserved (and that will be
altered by your procedure) on the stack and pop them off when the
procedure returns.
The <reglist> parameter is a list of one or more registers. Separate
multiple registers with spaces.
Since you seem to want changes to ESI made in the function NextScore to be seen by the calling function you will want to remove the USES statement from that procedure. Change:
NextScore PROC USES esi
to:
NextScore PROC
Now when you increment ESI in next score it won't be undone when the function exits.
Another issue is that the lengthof pseudo-opcode does:
lengthof: Returns the number of items in array variable.
It may not be clear but this pseudo-opcode is the number of elements in the array when the code was assembled. You define the array of scores like this:
scores DWORD MAXIMUMSCORES DUP(0)
The scores array will always have a lengthof value of MAXIMUMSCORES. Rather than use lengthof what you should be doing is simply using the ESI register. You already use ESI to keep a count of elements you have added to the array. So this code:
WriteScore PROC USES esi ; Thought was somehow make esi global?
mov eax, lengthof scores ; total number of items added to array
call WriteInt
Can be changed to:
WriteScore PROC USES esi ; Thought was somehow make esi global?
mov eax, esi ; esi = number of items added to array
call WriteInt
Another issue is that it appears you don't know how the loop instruction works. From the [x86 Instruction Set] the loop instruction does:
Performs a loop operation using the ECX or CX register as a counter.
Each time the LOOP instruction is executed, the count register is
decremented, then checked for 0. If the count is 0, the loop is
terminated and program execution continues with the instruction
following the LOOP instruction. If the count is not zero, a near jump
is performed to the destination (target) operand, which is presumably
the instruction at the beginning of the loop.
In your code you never set ECX to the number of times you wish to loop, so it will use whatever value happens to be in ECX . This is why you have a lot of extra numbers printed out. ECX needs to be initialized. Since you want to loop through all the scores entered, you simply move ESI to ECX. Your WriteScore function did:
mov esi,0
L1:
mov eax, scores[esi *4]
call WriteInt ; writes the numbers in the array
inc esi
loop L1
We can modify it to be:
mov ecx,esi ; Initialize ECX loop counter to number of scores added
; to the array.
mov esi,0
L1:
mov eax, scores[esi *4]
call WriteInt ; writes the numbers in the array
inc esi
loop L1
Now we just loop through the number of scores (ESI) the user actually entered.
With these changes in mind your program could look something like this:
INCLUDE Irvine32.inc
INCLUDELIB Irvine32.lib
INCLUDELIB user32.lib
INCLUDELIB kernel32.lib
MAXIMUMSCORES equ 20
.data
scores DWORD MAXIMUMSCORES DUP(0)
optionPromptMsg byte "Type 1 to continue or -1 to exit: ", 0
scorePromptMsg byte "Enter your numeric score (0-100): ", 0
scoreErrorMsg byte "Score out of range (0-100)!", 0
optionErrorMsg byte "Only 0 or 1 allowed in option specification!", 0
resultMsg byte " scores have been entered.", 0
.code
main proc
mov esi, 0
L1:
mov edx, offset optionPromptMsg
call WriteString
call ReadInt
mov ebx, 1
cmp eax, ebx
je L2
mov ebx, -1 ; 99% sure my main is okay
cmp eax, ebx
je L3
mov ebx, -2
mov ecx, 2
cmp ebx, eax
ja L4
cmp eax, ecx
ja L4
L2: call NextScore
jmp L5
L4: mov edx, offset optionErrorMsg
call WriteString
call Crlf
jmp L5
L5:
loop L1
L3: call WriteScore
exit
main ENDP
WriteScore PROC USES esi ; We make changes to ESI not visible to caller
; since we don't intend to change the number of scores
; with this function. Any change to ESI in this function
; will not appear to the caller of this function
mov eax, esi ; total number of items added to array in ESI
call WriteInt
mov edx, offset resultMsg
call WriteString
mov ecx,esi
mov esi,0
L1:
mov eax, scores[esi *4]
call WriteInt ; writes the numbers in the array
inc esi
loop L1
mov eax, 5000
call Delay
ret
WriteScore ENDP
NextScore PROC ; We want changes to ESI to exist after we exit this function. ESI
; will effectively act as a global register.
mov edx, offset scorePromptMsg
call WriteString
call ReadInt
mov ebx, 0
mov ecx, 100
cmp ebx, eax
ja L1
cmp eax,ecx
ja L1
jmp L2
L1:
mov edx, offset scoreErrorMsg
call WriteString
call Crlf
L2:
mov scores[esi*4], eax ; gets the next number and puts it in the array
inc esi
ret
NextScore ENDP
END
Sample output of this:
Type 1 to continue or -1 to exit: 1
Enter your numeric score (0-100): 10
Type 1 to continue or -1 to exit: 1
Enter your numeric score (0-100): 20
Type 1 to continue or -1 to exit: 1
Enter your numeric score (0-100): 40
Type 1 to continue or -1 to exit: 1
Enter your numeric score (0-100): 650
Score out of range (0-100)!
Type 1 to continue or -1 to exit: 99
Only 0 or 1 allowed in option specification!
Type 1 to continue or -1 to exit: 1
Enter your numeric score (0-100): 100
Type 1 to continue or -1 to exit: -1
+5 scores have been entered.+10+20+40+650+100
I am trying to convert a C function to NASM in 32-bit.
Here is the C function
void shellsort (int *A, int n)
{
int gap, i, j, temp;
for (gap = n/2; gap > 0; gap /=2)
{
for (i= gap; i<n; i++)
{
for (j=i-gap; j>=0 && A[j] > A[j+gap]; j-=gap)
{
temp = A[j];
A[j] = A[j+gap]
A[j+gap] = temp;
}
}
}
}
Here is what I have done so far
%include "asm_io.inc"
SECTION .data
SECTION .bss
SECTION .text
extern printf
global sort
sort:
push ebp ;set up stack frame
mov ebp, esp
sub esp, 0x10 ;assign 16byte space for local variables
firstloop:
mov eax, [ebp+8] ;moves value of n into eax
mov edx, 0 ;prepares for division, remainder init
mov ecx, 2 ;divisor
div ecx ;division, store gap in eax
cmp eax, 0 ;compare if gap with zero
jle firstloopDone ;firstloopDone
firstloopDone:
div ecx
jmp done
secondloop:
mov ecx, eax ;copy value of gap into ecx, i=gap
cmp ecx, [ebp+8] ;compare i with 1st parameter n
jge secondloopDone ;jump to secondloop if greater-equal
inc ecx ;increment i
jmp thirdloop ;jump to thirdloop
secondloopDone:
inc ecx ;increment i
jmp firstloop ;jump to firstloop
thirdloop:
mov edx, ecx ;save i value
sub ecx, eax ;subtract gap from i and store in ecx
cmp ecx, 0 ;compare j with zero
jl thirdloopDone ;if not j>=0, then skip to end of loop
cmp [ebp+12+ecx], [ebp+12+ecx+eax] ;compare A[j] and A[j+gap]
jle thirdloopDone
sub ecx, eax ;subtract gap from j and store in ecx
jmp swap
thirdloopDone:
sub ecx, eax ;j-gap
jmp secondloop ;jump to second loop
swap:
mov edx, [ebp+12+ecx] ;copy A[j] to temp
mov [ebp+12+ecx], [ebp+12+ecx+eax] ;A[j]=A[j+gap]
mov [edp+12+ecx+eax], edx ;A[j+gap]= temp
jmp thirdloop
done:
leave ;destroy stack frame
ret
Needless to say, it doesn't work.
The error message says:
"error: beroset-p-603-invalid effective address"
at line 58 (thirdloop)
cmp [ebp+12+ecx], [ebp+12+ecx+eax]
and at line 71 (swap)
mov edx, [ebp+12+ecx]
I understand that this may be the wrong method. But from the nested loop in C code, I have too many variables to keep so I cant spare any of the registers.
Also, I suspect I may not have a proper understanding of the stack frame, which may show in my code.
Any help, or bug-discovery, would be greatly appreciated. Thanks.
You say you don't have enough registers to play with but you don't even use EBX, ESI, nor EDI. You made provisions for local variables yet you don't use any.
firstloop:
mov eax, [ebp+8] ;moves value of n into eax
mov edx, 0 ;prepares for division, remainder init
mov ecx, 2 ;divisor
div ecx ;division, store gap in eax
The previous code does not need the division. Simplify it with a shift operation. shr eax,1
The next code will always terminate. Use jle done
cmp eax, 0 ;compare if gap with zero
jle firstloopDone ;firstloopDone
firstloopDone:
div ecx
jmp done
Aren't parameters pushed from right to left?
mov ebx, [ebp+8] ;pointer to array A
firstloop:
mov eax, [ebp+12] ;number of elemnets n
You would typically have to code
swap:
mov edx, [ebx+ecx*4] ;TEMP=A[j]
add ecx,eax
xchg edx, [ebx+ecx*4] ;Swap A[j+gap] with TEMP
sub ecx, eax
mov [ebx+ecx*4], edx ;A[j]=TEMP
jmp thirdloop
I am trying to make an assembly program for finding GCD, takes in two integers, and prints out the GCD. The code assembles fine, however the program gets stuck in an infinite loop after both integers have been submitted:
;Title - GCD
INCLUDE Irvine32.inc
.data
strA BYTE "Enter an integer A: ",0
strB BYTE "Enter an integer B: ",0
temp DWORD ?
finalStr BYTE "GCD of the two integers is: ",0
.code
main PROC
call Clrscr
mainLoop:
mov edx,OFFSET strA
call WriteString
call ReadInt
mov temp, eax
call Crlf
mov edx, OFFSET strB
call WriteString
call ReadInt
mov ebx, eax
mov eax, temp
call Crlf
call GCD
mov edx, OFFSET finalStr
call WriteString
call WriteInt
call WaitMsg
jmp mainLoop
main ENDP
;-----------------------------------------------
abs PROC
; Computes the absolute value of a number.
; Receives: eax = the number
; Returns: eax = absolute value of the number
;-----------------------------------------------
cmp eax, 0 ; see if we have a negative number
jge done
neg eax
done:
ret
abs ENDP
;-----------------------------------------------
gcd PROC
; Finds Greatest Common Divisor of two integers
; Recieves: eax= int A , ebx = int B
; Returns eax = GCD
;-----------------------------------------------
call abs ;takes absolute value of both registers
mov temp, eax
mov eax, ebx
call abs
mov eax, temp
cmp eax, ebx ; making sure we divide the bigger number by the smaller
jz DONE ; if numbers are equal, GCD is eax either way
jc SWITCH ;swaps if ebx is larger then eax
mov edx, 0
SWITCH: ;swaps values so eax is larger then ebx
mov temp, eax
mov eax, ebx
mov ebx, temp
mov edx, 0
jmp L1
L1: ;divides until remainder is 0, then eax is GCD
div ebx
cmp edx, 0
jz DONE
mov eax, edx
jmp L1
DONE:
gcd ENDP
END main
How can I get out of this loop?
I see one problem straight up:
call abs ;takes absolute value of both registers
mov temp, eax
mov eax, ebx
call abs
mov eax, temp
There's nothing there moving the abs-ed ebx value back into ebx, it should be:
call abs ;takes absolute value of both registers
mov temp, eax
mov eax, ebx
call abs
mov ebx, eax
mov eax, temp
Another issue, though not directly related to your problem, is that the x86 architecture has long had an xchg instruction that would greatly clean up your code, specifically the use of temp.
And think about this sequence:
cmp eax, ebx ; making sure we divide the bigger number by the smaller
jz DONE ; if numbers are equal, GCD is eax either way
jc SWITCH ;swaps if ebx is larger then eax
mov edx, 0
SWITCH: ;swaps values so eax is larger then ebx
You'll find that the code at SWITCH: is going to be run regardless of which value is larger.
You can fix that by changing:
jc SWITCH ; swaps if ebx is larger then eax
mov edx, 0
into:
jc SWITCH ; swaps if ebx is larger then eax
mov edx, 0
jmp L1
Beyond that, I'm going to suggest actually running that code in your head to get an understanding of how it works but, more importantly, how to think like a machine in such a way that you become a better developer.
Start with the following table:
[temp] eax ebx edx <stack>
-------- -------- -------- -------- --------
? ? ? ? ?,
Then execute each line in turn, filling in the columns as data changes.
You'll eventually figure out where the problem is coming from and you'll also understand why sometimes the older guys that only ever had this means to debug are much better at it :-)
I'll give you a clue. Keep a particular eye on edx and see how that's changing, and how it's likely to affect certain other instructions, like div, that may depend on it being zero.