Why is my Assembly JUMP if below (JB) not working? - arrays

[Solved]
My code should find the minimum value in an array.
In a procedure shown below, the assembly code compares 2 values in an dword array and jumps to setMin part when the second value is below the first. For some reason my program does not jump to setMin even when I can clearly see in the registers during debugging that it should jump.
In result - the min value that is printed out is completely off and I don't know where its from (but that's entirely a different issue).
Here is the code:
; Find the minimum value in the array
; in : arr of 10 dword values
; minIs = "Minimum value is "
; out : eax
;----------------------------------
mov esi, OFFSET arr ; arr adress
mov eax, [esi] ; current min is 1st value
mov ecx, 10 ; loop counter
Lcompare:
add esi, 4 ; next value
mov ebx, [esi]
cmp eax, ebx ; compare 2 values
jb setMin ; Jump if not Below to next
jmp next
setMin:
mov eax, [esi] ; sets new minimum value
next:
loop Lcompare
mov edx, OFFSET minIs
call WriteString ; "Minimum value is "
call WriteInt ; display min value
call Crlf
The arr input: 194, 102, 167, 157, 107, 140, 158, 148, 173, 194
The output:
Minimum value is +1701602643
I would really appreciate if someone could point out why the program does not enter setMin: after jb setMin but instead goes straight to next:.
And as a bonus could someone explain why I am getting that output instead of one of the values from the array?

The comments under the question pointed out my mistakes.
What was wrong:
iterating in the loop 10 times rather than 9. The extra iteration grabbed a string from "the future"
I was finding the maximum rather than the minimum. Switching to cmp ebx, eax solved that.
The FULL working corrected code:
INCLUDE Irvine32.inc
.data
arr dword 10 dup(0)
prompt byte "Select #: "
values byte "The values are: ", 0
comma byte ", ", 0
minIs byte "Minimum value is ", 0
.code
part3 PROC
; Find the minimum value in the array
; in : arr of 10 values
; minIs = "Minimum value is "
; out : eax
;----------------------------------
; use jump if below JB
mov esi, OFFSET arr ; arr adress
mov eax, [esi] ; current min is 1st value
mov ecx, 9 ; loop counter
Lcompare:
add esi, 4 ; next value
mov ebx, [esi]
cmp ebx, eax ; compare 2 values
jnl next ; Jump if not less to next
mov eax, [esi] ; sets new minimum value
next:
loop Lcompare
mov edx, OFFSET minIs
call WriteString ; "Minimum value is "
call WriteInt ; display min value
call Crlf
ret
part3 ENDP
main PROC
mov edx, OFFSET prompt
call WriteString
Call part3
main ENDP
END main

Related

how to display the count in MASM?

this is my code for a MASM assignment, the assignment is:
Write an application that does the following:
Declare a 32-bit integer array with 50 elements with uninitialized values
Fill the 32-bit array with 50 random integers
Loop through array, and display each value, and count the number of negative values
After final loop finishes, display the count
I have it doing everything but displaying the count of negative numbers. If anyone can tell me where I am wrong that would be super helpful I have been working on this for some time.
INCLUDE Irvine32.inc
.data
myArray SWORD 50 DUP(?)
count DWORD 0
.code
main proc
call randomize
mov esi, offset myArray
mov ecx, lengthof myArray
L1:
call Random32
call WriteInt
call Crlf
mov [esi], eax
add esi, 4
loop L1
mov eax, offset myArray
mov esi, lengthof myArray
L2:
cmp dword ptr[esi], 0
jge L3
DWORD count
L3:
add esi, 4
loop L2
mov eax, count
call WriteDec
call Crlf
exit
main ENDP
END main

x86 Assembly - need assistance with program that sorts two given arrays with bubble-sort

I'm making a x86 Assembly program that utilizes the bubble-sort algorithm detailed in the Irvine Assembly textbook (for x86 systems) to sort two given arrays in ascending order. The second of my procedures searches each of the arrays and outputs the greatest value in each. I know that I have forgotten a jump command (sortAscending procedure), though I am unsure as to where to place it to accomplish what I need to do. When I go to build my ASM file in VS2022, I receive the following error:
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\BuildCustomizations\masm.targets(50,5): error MSB3721: The command "ml.exe /c /nologo /Sg /Zi /Fo"Debug\Template.obj" /Fl"Project.lst" /I "C:\irvine" /W3 /errorReport:prompt /TaTemplate.asm" exited with code 1.
I'm not sure if there are any other errors, but if someone notices them, please point them out. My code is shown below.
TITLE Sort Arrays, Version 1 (SortArrays.asm)
; This program sifts through and
; sorts arrays (in place) in ascending
; order and outputs the greatest value
; in each array.
; Name: Kasizah
; Date: 10-19-2022
INCLUDE Irvine32.inc
.data
Array1 DWORD 0C0D12AFh, 00030256h, 0FFAABBCCh, 0F700F70h, 00000000h, 0E222111Fh, 0ABCDEF01h, 01234567h
Array2 DWORD 61A80000h, 024F4A37h, 0EC010203h, 0FAEEDDCCh, 2C030175h, 84728371h, 63AA5678h, 0CD454443h, 22222222h, 61B1C2D3h, 7A4E96C2h, 81002346h, 0FDB2726Eh, 65432100h, 0FFFFFFFFh
; message strings
largestUnsignedS BYTE "The largest unsigned value in the array is: ",0
largestUnsignedF BYTE ".",0
.code
main PROC
mov esi,OFFSET Array1 ; point to start of Array1
mov ecx,LENGTHOF Array1 ; get length of Array1
call sortAscending ; sort Array1
; display sorted array using DumpMem
mov esi,OFFSET Array1 ; starting OFFSET
mov ecx,LENGTHOF Array1 ; number of units
mov ebx,TYPE Array1 ; doubleword format
call DumpMem
; get greatest value in Array1
mov esi,OFFSET Array1 ; point to start of Array1
mov ecx,LENGTHOF Array1 ; get length of Array1
call Crlf ; skip line
call getLargest ; display largest value in Array1
call Crlf ; skip line
mov esi,OFFSET Array2 ; point to start of Array2
mov ecx,LENGTHOF Array2 ; get length of Array2
call sortAscending ; sort Array2
; display sorted array using DumpMem
mov esi,OFFSET Array2 ; starting OFFSET
mov ecx,LENGTHOF Array2 ; number of units
mov ebx,TYPE Array2 ; doubleword format
call DumpMem
; get greatest value in Array2
mov esi,OFFSET Array2 ; point to start of Array2
mov ecx,LENGTHOF Array2 ; get length of Array2
call Crlf ; skip line
call getLargest ; display largest value in Array2
call Crlf ; skip line
exit ; exit the program
main ENDP
;-------------------------------------------------------
sortAscending PROC
;
; Sorts an array of 32-bit signed integers in ascending
; order using the bubble sort algorithm.
; Receives: pointer to array, array size
; Returns: nothing
;-------------------------------------------------------
mov edi,esi ; duplicate "point to first value"
; (used to reset ESI)
dec ecx ; decrement ECX (count) by 1
outer_loop:
push ecx ; save outer loop count
mov esi,edi ; point to first value
sort:
mov eax,esi ; get current array value
cmp [esi+4],eax ; compare [ESI] and [ESI+4]
jg next ; if [ESI] <= [ESI+4], no swap
xchg eax,[esi+4] ; else swap pair
mov [esi],eax
next:
add esi,4 ; move both pointers forward
loop sort ; inner loop
pop ecx ; retrieve outer loop count
loop outer_loop ; else repeat outer loop
quit:
ret
sortAscending ENDP
;-------------------------------------------------------
getLargest PROC
;
; Searches an array for its largest
; value.
; Receives: ESI - pointer
; Returns: statement of largest value in array
;-------------------------------------------------------
sift:
mov eax,[esi] ; move [ESI] into EAX
cmp [esi+4],eax ; compare EAX with [ESI+4]
jg next ; if EAX >= [ESI+4] don't replace
mov eax,[esi+4] ; mov [ESI+4] into EAX
next:
add esi,4 ; move pointer forward
cmp ecx,esi ; make sure that esi isn't at the end of array
je quit ; jump to quit if at the end of array
loop sift ; else return to sift loop
quit:
mov ebx,eax ; move EAX into EBX temporarily
mov eax,largestUnsignedF ; move largestUnsignedF string into EAX
call WriteString ; display largestUnsignedF string
mov eax,ebx ; move EBX into EAX
call WriteInt ; display EAX
mov eax,largestUnsignedS ; move largestUnsignedS string into EAX
call WriteString ; display largestUnsignedS string
ret
getLargest ENDP
END main
mov eax,esi ; get current array value
You forgot the square brackets to load the value stored at address ESI:
mov eax, [esi]
The getLargest code can simple fetch the last array element given that the array got sorted ascendingly.
mov eax, [esi + ecx * 4 - 4]
Also your messages talk about unsigned results, but the sorting algorithm treats the elements as signed dwords! For unsigned, use ja (JumpIfAbove).
Your sift code has errors:
cmp ecx,esi ; make sure that esi isn't at the end of array
je quit ; jump to quit if at the end of array
loop sift ; else return to sift loop
quit:
The cmp ecx, esi compares a count with an address! Can't work! Just drop these 2 lines.
The loop instruction would be enough provided you pre-decrement ECX before starting the loop and that you fetch the result via mov ebx, [esi].
A code that finds the largest element could look like this:
mov eax, 80000000h ; Smallest signed dword
More:
cmp [esi], eax
jng Skip
mov eax, [esi]
Skip:
add esi, 4
dec ecx
jnz More
mov ebx, eax ; The signed maximum

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

Calculating average of floats rounded to an integer NASM

Basically the program is suppose to input floating point numbers from the user, then get the sum of them, the average of them (from 0.0 to 100.0), anything above is not counted and anything below isn't counted as well. A negative number will not just not be counted but also exit the loop/program. The sum is outputted, number of valid numbers inputted is also outputted, and finally the average of the floating point numbers ROUNDED to nearest integer is outputted. I think I am close right now, my program thus far is below. Please, I could use some help finding the bug/(error in reasoning, if it exists) in my code below. Note I am using NASM.
Update: When it runs and input just the simple 1,2,3,4 it should output 3(rounded to nearest integer) is the average, but it outputs 100, which means it is running all the way through that loop or something similar is going wrong.
Update: I found the problem(s) there should have been a couple more comparisons and should have had more ffree st0 to free up st0, that kept creating a problem. Anyways got it working.
; Declare needed external functions
;
extern printf ; the C function, to be called
extern scanf ; the C function, to be called
SECTION .data ; Data section, initialized variables
three: dq 3.0
erroro: db "Did not work as intended...", 10, 0
zero: dq 0.0
half: dq 0.5
max_val: dq 100.0 ; max value is 100
min_val: dq 0.0 ; min value is 0
input: db "%lf", 0 ; input format
intro: db "Program title is Average and programmer is Joe", 10, 0 ; introduction format
iname: db "Enter your full name", 10, 0 ; get name instructions from user format
gname: db "%s %s", 0 ; get first and lats name
inst: db "Enter a number between 0.0 and 100.0, negative number to exit", 10, 0 ; instruction format
countout: db "The number of valid numbers entered: %d", 10, 0 ; counter output format
sumoutput: db "The sum is: %f", 10, 0 ; sum output format
avgoutput: db "The average is: %d", 10, 0 ; average output format
specialbye: db "You didn't enter any numbers, something wrong %s %s?", 10, 0 ; special goodbye format (if user did not input any valid numbers)
bye: db "Thanks for using this program, %s %s", 10, 0 ; goodbye output format
SECTION .bss ; BSS, uninitialized variables
x: resq 1 ; number entered by user, set to 0
sum: resq 1 ; sum variable is set to 0
avgc: resq 1 ; average used to compare (not what is outputted)
avg: resd 101 ; an array that is used for lookup value of average (it is how it outputs an integer)
count: resd 1 ; counter set to 0
avga: resq 201 ; an array of values from .5 to 100 (by increments of .5) used for comparing avg
fn: resw 10 ; first name set to 0, with a size of 10 words
ln: resw 10 ; last name set to 0, with a size of 10 words
SECTION .text ; Code section.
global main ; the standard gcc entry point
main: ; the program label for the entry point
push ebp ; set up stack frame
mov ebp,esp
mov eax, 0
mov ebx, 0
mov ecx, 101
.setavg: ;average array set up (used to output)
mov [avg+ebx], eax
push eax
mov eax, ebx
add eax, 4
mov ebx, eax
pop eax
add eax, 1
loop .setavg
finit
; get the first one taken care of...
fld qword [zero]
fstp qword [avga+0]
mov ebx, 0
mov ecx, 200
.setavga: ; average array set up to compare average values and then round to nearest integer (sort of..., actually it uses the average array to set which integer to "round to")
fld qword [avga+ebx]
fld qword [half]
fadd
mov eax, ebx
add eax, 8
mov ebx, eax
fstp qword [avga+ebx]
loop .setavga
jmp .start ; skip to .start label, used for testing purposes to reduce user input during testing
; output introduction
push dword intro
call printf
add esp, 4
; output asking for name
push dword iname
call printf
add esp, 4
; get first and last name
push dword ln
push dword fn
push dword gname
call scanf
add esp, 12
; start loop
.start:
; output number input instructions to user
push dword inst
call printf
add esp, 4
;get number
push dword x
push dword input
call scanf
add esp, 8
;compare x and minimum value
;mov eax, [x] ; put x in eax
;cmp eax, min_val ; compare x and minimum value
;jl .post_while ; if x is less than minimum value (which means it is a negative), jump to post while
; compare value to minimum value, if minimum is greater than user input, jump to post_while
fld qword [x]
fld qword [min_val]
fcomip st0, st1
ja .post_while
; free up st0
ffree st0
;compare x and max value
;mov eax, [x] ; put x in eax
;cmp eax, max_val ; compare x and max value
;jg .start ; if x is greater than max value, jump up to start (loop to start)
; compare against max value, if max is less than input jump back to .start label without counting it
fld qword [x]
fld qword [max_val]
fcomip st0, st1
jb .start
;free up st0
ffree st0
; else calculate sum
;mov eax, [sum]
;add eax, [x]
;mov [sum], eax
; calculate sum
fld qword [sum]
fld qword [x]
fadd
fstp qword [sum]
; update counter
mov eax, [count]
add eax, 1
mov [count], eax
jmp .start ; loop back to start
; after loop
.post_while:
; special check: if count = 0 that means no valid numbers have been received by user, then jump down to .special label
;mov eax, [count]
;mov ebx, 0
;cmp eax, ebx
;je .special
; calculate the average (sum/count)
;mov eax, [sum]
;mov ebx, [count]
;cdq
;div ebx
;mov [avg], eax
; calculate average
fld qword [sum]
fild dword [count]
fdiv
fstp qword [avgc]
; calculate rounding (closer to below or above) i.e. 1.3 is closer to 1 so avg is 1
;mov eax, edx
;mov ebx, 2
;mul ebx
;cmp eax, [count]
;jnge .dontadd ; if not greater i.e. 1.3, then jump down to .dontadd label
; else add one to the average
;mov eax, [avg]
;add eax, 1
;mov [avg], eax
; setup counter and index counters
mov ecx, 201
mov esi, 0
mov edi, 0
.roundloop: ; loop that rounds the average to nearest integer
; caluclate if going to increase edi (which is used for avg array *integer array*), if ecx is divisible by 2, then increase it.
mov eax, ecx
mov ebx, 2
cdq
div ebx
mov ebx, 0
cmp edx, ebx
jne .dontinc
inc edi
.dontinc:
; calculate if avga at index esi is above avgc (average calculate value), if so found where it is and end loop
fld qword [avgc]
fld qword [avga+esi]
;fld qword [three]
fcomip st0, st1
ja .endrloop
; increment esi by 8
mov eax,esi
mov ebx, 8
add eax, ebx
mov esi, eax
loop .roundloop
.endrloop:
mov ebx, edi ; save edi index of avg (integer array)
; means it is not closer to next integer
.dontadd:
push ebx
; output count
push dword [count]
push dword countout
call printf
add esp, 8
pop ebx
push ebx
; output sum
push dword [sum+4]
push dword [sum]
push dword sumoutput
call printf
add esp, 12
; output average
pop ebx
mov eax, ebx
mov ebx, 4
mul ebx
mov ebx, eax
push dword [avg+ebx]
push dword avgoutput
call printf
add esp, 8
jmp .regular ; output should be normal, since we got to here.
; special case where count == 0, meaning no valid numbers have been inputted
.special: ; not used in testing purposes at the moment
; output special goodbye message
push dword ln
push dword fn
push dword specialbye
call printf
add esp, 12
jmp .small ; now small jump to skip rest of output
.regular:
; output regular bye message
push dword ln
push dword fn
push dword bye
call printf
add esp, 12
; small jump used only in special case
.small:
mov esp, ebp ; takedown stack frame
pop ebp ; same as "leave" op
mov eax,0 ; normal, no error, return value
ret ; return
Glad you found a solution!
Could you tell us why you did the following the hard way?
.setavg: ;average array set up (used to output)
mov [avg+ebx], eax
push eax
mov eax, ebx
add eax, 4
mov ebx, eax
pop eax
add eax, 1
loop .setavg
Usual version
.setavg: ;average array set up (used to output)
mov [avg+ebx], eax
ADD EBX, 4
add eax, 1
loop .setavg

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