Root
Root

Reputation: 47

Cannot understand why assembly isnt working output doesnt show correct result even tho value is copied correctly from the value register

I am trying to build a program in emu8086 which would be given as input 1 8 bit binary number then display in the output the hexadecimal form of it.

My code is this:

data segment

ends

stack segment
    dw   128  dup(0)
ends

code segment
start:



mov ax,data
mov ds,ax
mov cx,0
mov bl,8

input:

mov ah,07h
int 21h

cmp al,46

sub al,30h
cmp al,0

je valid

cmp al,1

je valid

jmp input

valid:

cmp bl,0

je exit

sub bl,1

shl cl,1

add cl,al

jmp input

exit:


mov bl,15

and bl,cl

cmp bl,0

je printzero

cmp bl,1

je printone

cmp bl,2


je printtwo


cmp bl,3


je printthree


cmp bl,4


je printfour


cmp bl,5


je printfive


cmp bl,6


je printsix


cmp bl,7



je printseven


cmp bl,8


je printeight



cmp bl,9



je printnine


cmp bl,10


je printa



cmp bl,11


je printb


cmp bl,12


je printc


cmp bl,13


je printd


cmp bl,14


je printe


cmp bl,15


je printf


printzero:

mov dl,'0'

mov ah,02h

int 21h

jmp exit2



printone:


mov dl,'1'

mov ah,02h

int 21h

jmp exit2


printtwo:

mov dl,'2'

mov ah,02h

int 21h

jmp exit2



printthree:

mov dl,'3'


mov ah,02h

int 21h

jmp exit2



printfour:  

mov dl,'4'


mov ah,02h

int 21h

jmp exit2



printfive:

mov dl,'5'

mov ah,02h

int 21h

jmp exit2




printsix:


mov dl,'6'

mov ah,02h

int 21h

jmp exit2



printseven:

mov dl,'7'

mov ah,02h

int 21h

jmp exit2




printeight:

mov dl,'8'

mov ah,02h

int 21h

jmp exit2



printnine:

mov dl,'9'

mov ah,02h

int 21h

jmp exit2

printa:


mov dl,'A'

mov ah,02h

int 21h

jmp exit2



printb:

mov dl,'B'

mov ah,02h

int 21h

jmp exit2



printc:

mov dl,'C'

mov ah,02h

int 21h

jmp exit2



printd:

mov dl,'D'

mov ah,02h

int 21h

jmp exit2



printe:

mov dl,'E'

mov ah,02h

int 21h

jmp exit2


printf:

mov dl,'F'


mov ah,02h

int 21h

jmp exit2


exit2:  

mov bl,240

and bl,cl

cmp bl,0

je printzero2

cmp bl,1

je printone2

cmp bl,2


je printtwo2


cmp bl,3


je printthree2


cmp bl,4


je printfour2


cmp bl,5


je printfive2


cmp bl,6


je printsix2


cmp bl,7



je printseven2


cmp bl,8


je printeight2



cmp bl,9



je printnine2


cmp bl,10


je printa2



cmp bl,11


je printb2


cmp bl,12


je printc2


cmp bl,13


je printd2


cmp bl,14


je printe2


cmp bl,15


je printf2




printzero2:

mov dl,'0'

mov ah,02h

int 21h

jmp exit3



printone2:


mov dl,'1'

mov ah,02h

int 21h

jmp exit3


printtwo2:

mov dl,'2'

mov ah,02h

int 21h

jmp exit3



printthree2:

mov dl,'3'


mov ah,02h

int 21h

jmp exit3



printfour2:  

mov dl,'4'


mov ah,02h

int 21h

jmp exit3



printfive2:

mov dl,'5'

mov ah,02h

int 21h

jmp exit3




printsix2:


mov dl,'6'

mov ah,02h

int 21h

jmp exit3



printseven2:

mov dl,'7'

mov ah,02h

int 21h

jmp exit3




printeight2:

mov dl,'8'

mov ah,02h

int 21h

jmp exit3



printnine2:

mov dl,'9'

mov ah,02h

int 21h

jmp exit3

printa2:


mov dl,'A'

mov ah,02h

int 21h

jmp exit3



printb2:

mov dl,'B'

mov ah,02h

int 21h

jmp exit3



printc2:

mov dl,'C'

mov ah,02h

int 21h

jmp exit3



printd2:

mov dl,'D'

mov ah,02h

int 21h

jmp exit3



printe2:

mov dl,'E'

mov ah,02h

int 21h

jmp exit3


printf2:

mov dl,'F'


mov ah,02h

int 21h

jmp exit3 


exit3:




mov ax, 4c00h
int 21h  

ends

end start

I quickly realised that you dont need to store every input in a array , you can shift left by 1 the value of the register which would store the number then add the input (1 or 0).Well I expected my code to work but I came into a issue:

The register which stores the value of my number is CL.And it does correctly if I type 11110000 in this order the value of CL becomes F0 which is what I want.I put it through a AND mask of Fh to extract the 4 LSB and then through a AND mask of F0h to extract the 4 MSB however in the output I get 00.I honestly dont know where I am wrong.The value of BL register becomes the same with the value of the CL register but it still doesnt work.

Upvotes: 1

Views: 64

Answers (1)

Sep Roland
Sep Roland

Reputation: 39721

Necessary corrections

  mov bl,8
input:
  mov ah,07h
  int 21h
  cmp al,46
  sub al,30h
  cmp al,0
  je  valid
  cmp al,1
  je  valid
  jmp input
valid:
  cmp bl,0
  je  exit
  sub bl,1
  shl cl,1
  add cl,al
  jmp input
exit:
  • The cmp al,46 instruction serves no purpose. It is a left-over from some previous edit: just remove it.
  • The input loop runs 1 time too many! Luckily, you don't add the ninth input to the CL register. The reason is that you are checking for BL becoming 0 at the wrong place. See below.

You can simplify the checking, as well as the looping:

  mov  bl, 8
input:
  mov  ah, 07h   ; DOS.DirectSTDINInput
  int  21h       ; -> AL
  sub  al, 30h   ; Convert from character to number [0,9]
  cmp  al, 1     ; Everything other than [0,1] is ABOVE 1 ...
  ja   input     ; and so `JA input` will go repeat the input
  shl  cl, 1     ; Make room in CL
  add  cl, al    ; Add the new digit
  dec  bl        ; `DEC` already sets the flags
  jnz  input     ; Repeating 8 times means we jump back 7 times
exit:

exit:
  mov bl,15
  and bl,cl
  cmp bl,0
  je  printzero
  cmp bl,1
  je  printone

  ...

exit2:  
  mov bl,240
  and bl,cl
  cmp bl,0
  je  printzero2
  cmp bl,1
  je  printone2

  ...
  • You need to display the Most Significant Digit first! So at exit use the mask 240 to retrieve the High Nibble, and at exit2 use the mask 15 to retrieve the Low Nibble.
  • You didn't truly retrieve the High Nibble! What the and with 240 does is remove the Low Nibble from the BL register, but the High Nibble still remains where it was. This means that the value in BL is in {0,16,32,48,64,80,96,112,128,144,160,176,192,208,224,240}.

The quick fix then is to replace the series of cmp bl,? instructions:

exit:
  mov  bl, 240
  and  bl, cl
  cmp  bl, 0
  je   printzero2
  cmp  bl, 16
  je   printone2
  cmp  bl, 32
  je   printtwo2
  ...

exit2:
  mov  bl, 15
  and  bl, cl
  cmp  bl, 0
  je   printzero
  cmp  bl, 1
  je   printone
  cmp  bl, 2
  je   printtwo
  ...

First steps in improvement

Currently your program contains a lot of repeated instructions. Better solutions exist and I'm sure that the links provided by @PeterCordes will reveal these.
Nonetheless, short of doing the whole task myself, I suggest you study the below code and that you find out for yourself that this already simplifies the program a lot, and that it demonstrates how to write readable code. Mind you, I don't present this as the optimal solution:

  mov  bl, 8
input:
  mov  ah, 07h   ; DOS.DirectSTDINInput
  int  21h       ; -> AL
  sub  al, '0'   ; Convert from character to number [0,9]
  cmp  al, 1     ; Everything other than [0,1] is ABOVE 1 ...
  ja   input     ; and so `JA input` will go repeat the input
  shl  cl, 1     ; Make room in CL
  add  cl, al    ; Add the new digit
  dec  bl        ; `DEC` already sets the flags
  jnz  input     ; Repeating 8 times means we jump back 7 times
; ---------------
  mov  bl, 0F0h  ; High Nibble mask
  and  bl, cl    ; BL = {0, 16, 32, 48, ... , 208, 224, 240}
  mov  dl, '0'
  cmp  bl, 0
  je   printHigh
  mov  dl, '1'
  cmp  bl, 16
  je   printHigh
  
  ...
  
  mov  dl, 'E'
  cmp  bl, 224
  je   printHigh
  mov  dl, 'F'   ; No need for `CMP BL, 240`. It's 240 for sure
printHigh:
  mov  ah, 02h
  int  21h
; ---------------
  mov  bl, 0Fh   ; Low Nibble mask
  and  bl, cl    ; BL = {0, 1, 2, 3, ... , 13, 14, 15}
  mov  dl, '0'
  cmp  bl, 0
  je   printLow
  mov  dl, '1'
  cmp  bl, 1
  je   printLow
  
  ...
  
  mov  dl, 'E'
  cmp  bl, 14
  je   printLow
  mov  dl, 'F'   ; No need for `CMP BL, 15`. It's 15 for sure
printLow:
  mov  ah, 02h
  int  21h
; ---------------
  mov  ax, 4C00h
  int  21h  

Final steps in improvement

Because your program encompasses eight counts of character input from the keyboard as well as two counts of character output to the screen, an optimized version would focus on code-size rather than execution time. The time spent in those ten DOS functions represents almost 100% of the execution time!
Due to all the repeated instructions, your program currently contains 471 bytes. My 'first steps' solution already lowered this number to 255 bytes, but surely we can do (much) better?

Let's look at what the program does. You input eight binary digits, combine these into a single byte-sized value, and then immediately after, you decompose the byte into its two nibbles.
Very often a (considerable) speed gain or size reduction comes from radically changing the methodology we use. What I propose is to not have to decompose a byte into its two nibbles. The below snippets repeat twice, the input of only four binary digits that are converted into one hex character.

  • The first snippet uses a lookup table that contains the sixteen hex characters. This solution comes in at 55 bytes:
     ORG 256

     call @f              ; -> DH (AX BX)
     mov  dl, dh
     call @f              ; -> DH (AX BX)
     mov  ah, 02h         ; DOS.DisplayOutput
     int  21h             ; -> (AL)
     mov  dl, dh
     int  21h             ; -> (AL)
     ret                  ; Terminate in .COM program

@@:  mov  bx, 0010h       ; Initialize BX with a sentinel-bit set at index 4
     mov  ah, 07h         ; DOS.DirectSTDINInput
@@:  int  21h             ; -> AL
     sub  al, '0'         ; Convert from ["0","1"] to [0,1]
     shr  al, 1           ; Bring digit [0,1] into the carry flag
     jnz  @b              ; Every invalid key repeats the loop
     rcl  bl, 1           ; Bring new digit into BL
     jnc  @b              ; Repeat while sentinel-bit was not shifted out
     mov  dh, [list + bx] ; Fetch a hex character
     ret

lst: db   '0123456789ABCDEF'
  • The second snippet uses a very old and extremely clever calculation to convert from [0,15] into an hex character. This solution comes in at 39 bytes:
     ORG 256

     call @f              ; -> AL (AX BX)
     xchg dx, ax          ; Shorter code than `mov dl, al`
     call @f              ; -> AL (AX BX)
     push ax
     mov  ah, 02h         ; DOS.DisplayOutput
     int  21h             ; -> (AL)
     pop  dx
     int  21h             ; -> (AL)
     ret                  ; Terminate in .COM program

@@:  mov  bl, 00010000b   ; Initialize BL with a sentinel-bit set at index 4
     mov  ah, 07h         ; DOS.DirectSTDINInput
@@:  int  21h             ; -> AL
     sub  al, '0'         ; Convert from ["0","1"] to [0,1]
     shr  al, 1           ; Bring digit [0,1] into the carry flag
     jnz  @b              ; Every invalid key repeats the loop
     rcl  bl, 1           ; Bring new digit into BL
     jnc  @b              ; Repeat while sentinel-bit was not shifted out
     xchg ax, bx          ; Shorter code than `mov al, bl`
     cmp  al, 10          ; \.
     sbb  al, 69h         ;  |. From [0,15] to {["0","9"],["A","F"]}
     das                  ;  /.
     ret                  ; AL now has a hex character

These optimized snippets were assembled using FASM.

Upvotes: 1

Related Questions