Related: on a modern x86 CPU in 16-bit mode, you can use SSE4.1 phminposuw
with a range-shift to make it work on signed inputs. You can find the min out of 8 elements in one instruction: x86 Assembly - 2 largest values out of 4 given numbers. Load signed bytes using pmovsxbw xmm0, [num]
.
A more-efficient way to implement the min-finding loop avoids the slow loop
instruction, and avoids taken branches (other than the loop branch) when there's no new min. I could have made the code smaller by using loop
, and/or by using a normally-taken jge
to skip over an instruction inside the loop, instead of falling through into code that's normally not run.
Related: Assembly program that finds second largest value in array. I used a similar loop structure there.
Comparing against an end-pointer is efficient, whether it's a constant or in a register. As an immediate, it saves instructions outside the loop. We also don't need to tie up CX
with a loop counter; we already have the pointer in SI
.
The main point of this answer is the more compact code for printing a leading -
, like I mentioned in comments on @Ahtisham's answer. See the 2nd part of main
below.
.model small
.stack 100h
.data
num db 0,5,-1,4,2 ;my array
numEnd:
numSize = numEnd - num
msg db "The min number is: $"
.code
main PROC
mov ax, @data
mov ds, ax
mov si, OFFSET num
mov bl, [si] ; bl = min
calMin: ; do {
cmp si, OFFSET numEnd-1
jae endloop ; if (p >= end-1) break;
inc si ; p++
cmp bl, [si]
jle calMin ; if (*p <= min) continue; signed compare
; on new min, fall through and update BL before continuing
mov bl, [si] ; min = *p
jmp calMin ; } while(1);
endloop:
;;; BL = min. Do whatever you want with it.
Now we have the min of the array in bl
.
Normally you'd load into a register instead of using multiple loads, but mov al, [si]
only runs rarely for most inputs. Reloading the same value is fast (because of cache) so saving an instruction in the common-case loop is a win even if it means an extra load when we find a new min.
Given a single-digit signed min
in bl
, we can print it (with a -
if it's negative):
; we could print the message before the loop
; if we wanted to avoid clobbering registers.
mov ah, 09h ; print a message first
mov dx, offset msg
int 21h ; al = write string to stdout(ds:dx)
mov dl, bl ; in case it's positive
neg bl ; bl = 0 - bl setting flags accordingly
jle posNum ; if (0 <= num) skip the leading '-'
mov ah, 02h
mov dl, '-' ; print minus symbol if negative number
int 21h ; al = print char to stdout(dl)
mov dl, bl ; the negated value is positive, print it
posNum:
; absolute value of single-digit number in DL
mov ah, 02h
add dl, '0'
int 21h ; al = print char to stdout(dl)
exit:
mov ax, 04c00h ; AL = 0 (exit status), AH = 4C (Exit function)
int 21h
main ENDP
END main
You don't need to test SF
and ZF
separately with separate js
and jz
instructions. I used jle
after a neg
to fall through if the original number was negative, because it sets flags based on 0 - num
. i.e. jle
is taken if 0 <= num
, i.e. num
is non-negative and we shouldn't print a '-'
.
I could have done
test bl, bl
jge posNum
because test same,same
sets flags identically to cmp bl, 0
. And of course jge
is taken for numbers that are greater than or equal to zero.
Note that 0 - 128
does overflow, so just testing SF after neg
isn't equivalent to jle
. jle
is correct, js
isn't.
But our printing only handles single-digit integers in the first place. If that's what you have, use one of the many multi-digit number functions to print the absolute value as an unsigned integer after printing the optional '-'
.