0

I wrote a program to get the smallest number in an array, but I can't print it on the screen :(( I think the problem is converting it to ASCII.

.model small
.stack 100h
.data
    x db 0,5,-1,4,2     ;my array
    min db 0            ;variable to store the smallest number
    msg db "The min number is: $"
.code
main PROC
    mov ax,@data
    mov ds,ax

    xor cx,cx
    mov cl,5          ;set count to 5

    mov si,0          ;start at index 0
L1:
    mov dl,x[si]     ;can't compare memory to memory so I moved x[si] to dl
    cmp min,dl       ;checks to see if the value in dl is smaller than 
                     ;value in min
    JNB no          
    mov min,dl       
no:
    inc si
    loop L1
    mov ah,09
    mov dx,offset msg    ;print the message
    int 21h

    cmp min,0            ;check if number is negative or positive
    JB minus

    mov ah,02
    mov dl,min
    int 21h

minus:                 ;if minus print the minus sign
    mov ah,02
    mov dl,'-'
    int 21h

    *mov ah,02
    mov dl,min
    or dl,30h        ;here's my error can't print value of min
    int 21h*

    mov ah,04ch      ;exit code
    int 21h
main ENDP
END main

Also if anyone has good source or a book of learning 80386 assembly protected mode please mention it here :))

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 2
    Questions on how to print integers in x86 assembly have already been asked lots of times, see e.g. http://stackoverflow.com/search?q=%5Bassembly%5D+print+number – Michael Apr 24 '17 at 17:04
  • emu8086 has built in debugger AFAIK (never used it myself, I mean the whole emu8086). Consider exploring it, as your find_min code has several shortcomings even if printing would work. Printing out decimal value is actually lot more tricky, either constraint yourself to values 0-9 only first, to make the algorithm work, or don't print anything and use only debugger to verify results directly in memory/registers. When you will get over basics, search for that value -> decimal ascii string examples, or start with hexa first, those are much easier to understand (the code of conversion). – Ped7g Apr 24 '17 at 17:08
  • Thank you guys a lot, I haven't coded in assembly in a while I forgot about the method of converting numbers to ascii . –  Apr 24 '17 at 17:25
  • 1
    I think the title of this qiestion should be "How to print negative number in 8086 assembly" – Ahtisham Nov 25 '17 at 02:50
  • 1
    emu8086 (I assume the tag is correct) also has macros for printing numbers. `print_num` (for signed) and `print_num_uns` (for unsigned). More on their usage can be found here: http://jbwyatt.com/253/emu/asm_tutorial_05.html – Michael Petch Nov 25 '17 at 03:04
  • @Ahtisham: I think you're right, the dup-target I used only does unsigned. Your answer here only handles single-digit numbers, but I guess that's all this question needs. – Peter Cordes Nov 25 '17 at 03:15
  • @PeterCordes Should the duplicate mark be removed then ? – Ahtisham Nov 25 '17 at 04:55
  • @Ahtisham: I couldn't find another question about printing negative numbers (other than `printf` of course), so reopening. – Peter Cordes Nov 25 '17 at 05:11
  • 1
    @PeterCordes I believe it is Sep Roland has a version that does signed numbers. (main difference is the check for the sign bit and the addition of a negative sign). I haven't searched but I know it exists. Edit: found it: https://stackoverflow.com/questions/42325964/unable-to-display-negative-numbers-using-array-in-the-following-8086-assembly-co – Michael Petch Nov 25 '17 at 05:18

2 Answers2

1

You code has two problems ( apart from displaying a negative number )

1.Placing zero in NUM variable

What if your array contains non-zero numbers like for eg: 5, 4, 7, 1 it should print 1 as its the minimum number but your code will print 0 which is not even present in the array.

2.Using JNB and JB Instructions

These Instruction are only used for positive integer here have a look JNB JB. You have to use JNG and JG as your array contains negative number.

Following is the program to check for minimum number in an array ( Doesn't matter if it contains negative or positive number it will work in both the cases and it also shows how to print negative number ):

    .model small
.stack 100h

.data

        num db 7,5,3,4,2      ;my array
        msg db "The min number is: $"               

.code

main PROC

         mov ax,@data
         mov ds,ax
         mov ax, 0
         mov bl, num [0]  ; store first element of array in bl
         mov cx, 4
         mov si, 1        ; index of second element

         calMin:

                mov al, num [si] 

                cmp bl, al       
                jng continue  ; jng is used for signed numbers

                mov bl, al      ; exchange values if smaller

                continue:   

                       inc si       

                loop calMin 

         mov ah, 09h
         mov dx, offset msg
         int 21h        

         mov cl, bl

         neg cl         ; 2's complement the min number ( to check if its negative or positive number )                         

         js posNum      ; jmp to posNum if sign flag is set

         mov bl, cl
         jz posNum                       

       negNum:

         mov ah, 02h
         mov dl, '-'     ; print minus symbol if negative number
         int 21h

       posNum:

         mov ah, 02h     
         mov dl, bl
         add dl, 48
         int 21h  

         exit:                                    

                mov ah,04ch      
                int 21h

main ENDP

END main
Ahtisham
  • 9,170
  • 4
  • 43
  • 57
  • BTW, you don't need to `xchg bl,al`, you can just use `mov bl,al` which is more efficient. Also, `js`/ `jz` to the same destination is silly: you want to jump if `cl` is now less than or equal to zero, and `neg cl` sets flags according to `0 - cl`, so `jle posNum`. – Peter Cordes Nov 25 '17 at 05:22
  • After printing a `-`, you can just `mov bl, cl` and fall through into `posNum` instead of duplicating the code to print a single-digit integer. – Peter Cordes Nov 25 '17 at 05:46
  • @PeterCordes that is what i am trying to do :) but it prints `/` because i am placing the complement of number in bl and printing it when array contains all non-zero positive numbers. – Ahtisham Nov 25 '17 at 05:46
  • placing `mov bl, cl` in between `js posNum` and `jmp negNum` two work for negative and positive number but not for zero it prints `-0` – Ahtisham Nov 25 '17 at 05:54
  • Then you put it in the wrong place. It should be in a block that's jumped over for non-negative numbers. – Peter Cordes Nov 25 '17 at 05:55
  • have to include `jz posNum` i don't find any other way. – Ahtisham Nov 25 '17 at 06:04
  • Use `jle` or `jge` instead of `js` jump on ZF or SF (actually `SF != OF`, but we know `OF` = 0 after `0 - x`. Actually, `0 - 128` does overflow, but that's not a single-digit number.) – Peter Cordes Nov 25 '17 at 06:41
1

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

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847