Assembly: Issues with Indexing - arrays

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

Related

Print array in Asembly x86

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].

NASM: Convert multi character input to decimal

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

Segmentation fault Core Dumped X86 Assembly Array

I asked for help earlier and thought I was home free but I'm not. My logic was wrong and I've greatly altered it. This program is supposed to return the Max int in the array (which also happens to be the last element). After tracing it with GDB a few times, I see that I get to the 5th (2nd to last) element in the array, "20", when I hit a segmentation fault and the program halts. I set ECX to the array length and subtracted 1 from ECX to avoid this, but my logic is obviously wrong. Am I wrong in depending on the ECX counter to terminate the loop. Any ideas please?
***EDIT MAJOR EDITS TO CODE
SECTION .data ;data section
msg1: db "Here are the array elements:", 10, 0
msg1Len: equ $-msg1
msg2: db "Here is the sorted array:", 10, 0
msg2Len: equ $-msg2
arr: dd 12, 16, 6, 18, 10, 40, 30
arrLen: equ ($-arr)/4 ;number of elements = array length / 4
SECTION .bss
max resd 1 ;declare and reserve space for max
SECTION .text
global bsort
bsort:
push ebp ; save old ebp register
mov ebp, esp ; build a new stack
restart:
mov ebx, arr ; the base address argument is saved in ebx register
mov ecx, arrLen ; the size argument is saved in exc register
sub ecx, 1 ; Last member has no following element to compare with.
; So we need to reduce the counter by 1
top:
mov eax, [ebx] ;; access first array element. Move its value to eax
cmp eax, [ebx+4] ; compare the value of eax ([ebx]) with [ebx+4]
jle noswap ; if value at eax is less or equal to value of [ebx+4]
; no need to exchang values. Jump to noswap
xchg eax, [ebx+4] ; if value at eax > value [ebx+4], exchange
mov [ebx], eax ; store the new exchanged value at [ebx]
jmp restart ; reset the base address and counter. Start over
noswap:
add ebx, 4 ; move to the next array element
loop top ; loop back to the top if the register ecx > 0
leave
ret
global main
main:
push ebp
mov ebp, esp
mov ecx, msg1 ;print msg1
mov edx, msg1Len
call PString
;save array base address in ebx and save sizein in ecx
mov ebx, arr
mov ecx, arrLen; store num elements in ecx
;loop to print array
PrintArray:
mov eax, [ebx] ;move value [ebx] to eax
call PrintDec
call Println
add ebx, 4
loop PrintArray
;call bubblesort
call bsort
mov ecx, msg2
mov edx, msg2Len
call PString
;save arr base add in sbx and size in ecx
mov ebx, arr
mov ecx, arrLen
PrintSortedArray:
mov eax, [ebx]
call PrintDec
call Println
add ebx, 4
loop PrintSortedArray
;exit program and clean stack
mov esp, ebp
pop ebp
ret
PString:; save register values of the called function
pusha
mov eax,4 ; use 'write' system call = 4
mov ebx,1 ; file descriptor 1 = STDOUT
int 80h ; call the kernel
; restore the old register values of the called function
popa
ret
Println:
;will call PString func
;will change content of ecx and edx
;need to save registers used by the main program
section .data
nl db 10
section .text
pusha
mov ecx, nl
mov edx, 1
call PString
;return original register values
popa
ret
PrintDec:
;saves all registers so they return unmodified
;build the function to handle dword size
section .bss
decstr resb 10 ; 10 32-bit digits
ct1 resd 1 ;keep track of dec-string size
section .text
pusha; save registers
mov dword[ct1],0 ;initially assume 0
mov edi, decstr ; edi points to dec-string
add edi, 9 ; moved to the last element of string
xor edx, edx ; clear edx for 64-bit div
whileNotZero:
mov ebx, 10 ; get ready to divide by 10
div ebx ; divide by 10
add edx, '0' ; convert to ascii
mov byte[edi], dl ; put it in string
dec edi ; move to next char in str
inc dword[ct1] ; inc char counter
xor edx, edx ; clear edx
cmp eax, 0 ;is remainder 0?
jne whileNotZero ;if no, keep on looping
inc edi ; conversion finished, bring edi
mov ecx, edi ; back to start of string. make ecx
mov edx, [ct1] ; point to counterm edx gets # chars
mov eax, 4 ; print to stdout
mov ebx, 1
int 0x80 ; call kernel
popa ; restore registers
ret

GCD Program in Assembly doesn't show the output of gcd

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.

I'm having problems adding numbers in an array -- x86 MASM assembly

I have to create a random range of 100 counts of numbers from 27 to 58 and then add up all the numbers in the 100 positions for a total amount. However, when I do that I get a random number and ninety-nine 32's as a result. I've searched everywhere and tried possible solutions but I'm either getting the same result or random garbage. Can someone offer some help?
INCLUDE irvine32.inc
.data
a DWORD 27
b DWORD 58
delta DWORD ?
source DWORD 100 DUP(?)
prompt BYTE "The sum of the 100 counts in array is ",0
.code
main PROC
Call Randomize
mov edi, 0
mov edi, OFFSET delta
mov esi, OFFSET source
mov eax, b
sub eax, a
inc eax
mov delta, eax
mov ecx, LENGTHOF source
mov eax, 0
L1:
mov eax, delta
call randomrange
add eax, a
mov source, eax
call writedec
mov al, " "
call writechar
loop L1
call crlf
call crlf
mov ecx, SIZEOF source
mov edx, OFFSET prompt
call writestring
l2:
add eax,[esi]
add esi, TYPE delta
call writedec
mov al, " "
call writechar
loop l2
exit
main ENDP
END main
I assume randomrange leaves its random number in EAX.
In the L1 loop, you add A to EAX to get your random value and then copy it to the first element of SOURCE every time through the L1 loop. That's why the first element is random, but the rest of the array isn't touched. (Note that you have the same problem in L2 -- you always get the value to print from the first element of SOURCE.)

Resources