user6871791
user6871791

Reputation:

Assembly 8086 printing a signed single-digit integer

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 :))

Upvotes: 0

Views: 4645

Answers (2)

Peter Cordes
Peter Cordes

Reputation: 365832

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

Upvotes: 1

Ahtisham
Ahtisham

Reputation: 10136

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

Upvotes: 1

Related Questions