-4

I am confused as to how arithmetic operations work for 4 digit numbers. This is for finals project I am working on.

I am really trying hard to understand Assembly so forgive me if I cannot answer your questions properly, but this is the code we used for Arithmetic Operations for 2 digit inputs. I was wondering how I can use it when the inputs are going to be for 4 digits.

.MODEL SMALL
.STACK 200
.DATA 
    RADIX DB 10     
    TEMP DB 10 DUP(?)
    CRLF    DB  0DH,0AH,'$'
    PROMPT1 DB  'Input the 1st 2digit number: ','$'
    PROMPT2 DB  'Input the 2nd 2digit number: ','$'
    PROMPT3 DB  'The sum of two numbers is: ','$' 
    PROMPT4 DB  'The difference of two numbers is: ','$'
    PROMPT5 DB  'The product of two numbers is: ','$'
    PROMPT6 DB  'The quotient of two numbers is: ','$'
    PROMPT7 DB  'The remainder of two numbers is: ','$'
    NUM1    DB  ?
    NUM2    DB  ? 
    NUM3    DB  ?
    SUM     DB  ?  
    DIFF    DB  ?
    PROD    DB  ?
    QUO     DB  ?
    REM     DB  ?    

.CODE
.STARTUP
    LEA DX,PROMPT1 
    MOV AH,09H      
    INT 21H 

    MOV AH, 01H
    INT 21H
    SUB AL, 30H
    MOV CH, AL 
    MOV AH, 01H
    INT 21H

    SUB AL, 30H
    MOV CL, AL 
    MOV AL, 10000B 
    MUL CH 
    XOR AH, AH
    OR AL, CL   
    MOV NUM1, AL
    MOV AL, 00H 

    LEA DX,CRLF
    MOV AH,09H
    INT 21H 

    LEA DX,PROMPT2
    MOV AH,09H
    INT 21H  

    MOV AH, 01H
    INT 21H
    SUB AL, 30H
    MOV CH, AL 
    MOV AH, 01H
    INT 21H

    SUB AL, 30H
    MOV CL, AL 
    MOV AL, 10000B 
    MUL CH 
    XOR AH, AH 
    OR AL, CL   
    MOV NUM2, AL

    ADD AL, NUM1 
    DAA
    ADD SUM, AL  

    LEA DX,CRLF
    MOV AH,09H
    INT 21H 

    LEA DX,PROMPT3
    MOV AH,09H
    INT 21H


    MOV AL, SUM 
    XOR AH, AH
    MOV BL, 10000B
    DIV BL
    AND AL, 0FH 
    ADD AL, 30H 

    LEA DL, AL  
    MOV AH, 02H
    INT 21H

    MOV AL, SUM
    XOR AH, AH   
    AND AL, 0FH
    ADD AL, 30H 

    LEA DL, AL
    MOV AH,02H   
    INT 21H  

    MOV AL,00H
    MOV AL,NUM1
    SUB AL,NUM2
    DAS
    MOV DIFF,AL 

    LEA DX,CRLF
    MOV AH,09H
    INT 21H

    LEA DX,PROMPT4
    MOV AH,09H
    INT 21H

    MOV AL, DIFF 
    XOR AH, AH 
    MOV BL, 10000B
    DIV BL 
    AND AL, 0FH
    ADD AL, 30H

    LEA DL, AL  
    MOV AH, 02H
    INT 21H

    MOV AL, DIFF 
    XOR AH, AH  
    AND AL, 0FH
    ADD AL, 30H

    LEA DL, AL
    MOV AH,02H     
    INT 21H                

    LEA DX,CRLF
    MOV AH,09H
    INT 21H

    LEA DX,PROMPT5
    MOV AH,09H
    INT 21H

    MOV AL, 00H
    MOV DL, 00H
    MOV AL, NUM1
    MOV DL, NUM2
    MUL DL 

    MOV PROD, AL   

    MOV CX, 00H 
    XOR BH, BH 
    XOR SI, SI   

    DISPX1:
    MOV DX, 00 
    DIV BX 
    MOV TEMP[SI], DL 
    INC SI
    INC CX 
    OR AX, AX 
    JNZ DISPX1
    DEC SI

    MOV AL, PROD
    DISPX2:
    MOV DL, TEMP [SI]
    MOV AH, 06H 
    ADD DL, 30H 
    INT 21H
    DEC SI
    DEC CX 
    JNZ DISPX2        

    MOV AX,00H 
    MOV BX,00H
    MOV AL,NUM1
    MOV BH,NUM2
    DIV BH
    MOV QUO,AL 
    MOV REM,AH

    LEA DX,CRLF
    MOV AH,09H
    INT 21H

    LEA DX,PROMPT6
    MOV AH,09H
    INT 21H

    MOV AL, QUO
    XOR AH, AH 
    MOV BL, 10000B
    DIV BL 
    AND AL, 0FH
    ADD AL, 30H

    LEA DL, AL 
    MOV AH, 02H 
    INT 21H

    MOV AL, QUO 
    XOR AH, AH 
    AND AL, 0FH
    ADD AL, 30H

    LEA DL, AL
    MOV AH,02H  
    INT 21H   

    LEA DX,CRLF
    MOV AH,09H
    INT 21H

    LEA DX,PROMPT7
    MOV AH,09H
    INT 21H     

    MOV AL, REM 
    XOR AH, AH 
    MOV BL, 10000B
    DIV BL 
    AND AL, 0FH 
    ADD AL, 30H 

    LEA DL, AL  
    MOV AH, 02H 
    INT 21H

    MOV AL, REM 
    XOR AH, AH   
    AND AL, 0FH
    ADD AL, 30H

    LEA DL, AL
    MOV AH,02H   
    INT 21H               

.EXIT                 
END
Abby
  • 1
  • 2
  • Please give more information, such as which number base you are using, and what you have tried so far. – Graham Asher Dec 02 '17 at 16:20
  • put some code here if you have tried any? – A user Dec 02 '17 at 16:24
  • I already added the code in there but it's for 2 digit input. – Abby Dec 02 '17 at 16:31
  • you can use `loop` instruction top get a 4 digit number by loading cx register with 4 http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#LOOP – Ahtisham Dec 02 '17 at 19:05
  • can you describe, how your current 2 digit code works? (what data types and algorithms you use) Looks like packed BCD, but I wonder how well you understand, what you have, it's not trivial to extend it to 4 digits, like copy paste of something, you will have to redesign the data and register usage a bit, to keep it BCD. – Ped7g Dec 02 '17 at 19:16
  • @Ped7g His code does not even work for 2 digit numbers. – Ahtisham Dec 02 '17 at 20:40
  • @Ahtisham did you try it, or why do you think it does not work? I did just very quick check in head, but it looks OK to me. Quite ugly (opinion based of course), and I'm not sure why BCD is being lectured at all, but didn't see any obvious bug. (then again, as this is BCD based, asking about how to extend 2 digits to 4 digits makes surprisingly lot of sense, at first I was like "hey, don't talk about digits, but about bits", but then I studied his source, and for BCD it makes sense to talk about digits, so for me this question is almost OK, just hard to help OP w/o writing book-answer). – Ped7g Dec 02 '17 at 22:09
  • @Ped7g The code asks the user to input a number, the number would be converted to ASCII. Then the first number would be the tens so it is multiplied to 10000B. This is the part I'm having a hard time to understand. Where do you multiply it when you need 4 digit numbers? What certain changes with the register usage do i need to make to make it print out the right answer? – Abby Dec 03 '17 at 00:26
  • @Ped7g Yeah i checked it prints wrong answer for add (try 21 + 2 it prints 86 or 84 i don't remember) and for others it prints garbage. – Ahtisham Dec 03 '17 at 02:14
  • @Ahtisham may be, there are some subtle bugs, which may not demonstrate under OP's environment. I'm not going to debug it all, as the code contains huge sins of coder who doesn't understand binary math, which causes me too big pain to read the code as whole, so I'm preparing answer tackling only the BCD thing, and few instructions from code. But the general idea/algorithm of the code is roughly OK. BTW, did you enter when testing that 21+2 as 21+02 I hope? The two digits inputs are mandatory. – Ped7g Dec 03 '17 at 02:19
  • @Ahtisham: Don't recommend `loop` to people that aren't already using it. [It's a mostly obsolete instruction](https://stackoverflow.com/questions/35742570/why-is-the-loop-instruction-slow-couldnt-intel-have-implemented-it-efficiently), and just extra instruction-set complexity compared to making loops with conditional branches (which you already need to know how to use to program in asm) – Peter Cordes Dec 03 '17 at 06:01

1 Answers1

2

You need to first study how integer values can be encoded in bits, i.e. binary encoding: https://en.wikipedia.org/wiki/Binary_number (it's a mandatory prerequisite to understand my answer and your problem).

The code asks the user to input a number, the number would be converted to ASCII.

Input is ASCII character. It is converted from ASCII to value 0..9 (in case of valid input, an ASCII digit). Because for example ASCII character '2' has value 50 (32h), so after sub al,48 you have in al value 2 (no more character, but integer value, encoded in binary).

... so far you managed to get at least one thing right, that there is some input from user. Everything else was messed up. If you want to code in assembly, you must be much more precise, the machine will never guess what you did want, it will always do what you asked it for in the code. If you are imprecise, it will do things you do not want.

Then the first number would be the tens so it is multiplied to 10000B.

Yes, the first digit value is multiplied by 16. This is the part where a kitten dies, because you used mul to multiply by power of two, instead of using left shift by 4 bits shl al,4. That's the same thing, as multiplying by 16, because 24 = 16 (if you understand binary numbers, you should be able to figure out, why shifting bits left is multiplying by powers of 2).

This is the part I'm having a hard time to understand. Where do you multiply it when you need 4 digit numbers?

Your original source is using packed-BCD encoding of two digit values.

First digit "tens" occupies top 4 bits, and second digits "ones" occupies low 4 bits. I.e. for user input "34" the resulting binary value is 0011 01002 = 3*16+4 = 52 in decimal, or 34h in hexa.

For two BCD digits you need 8 bits. Your old code does calculate the BCD value into al (8 bit register), and stores it into memory at NUM1 address, where a single byte (8 bits) is reserved.

Then when both values are in memory and second number is in AL, the code does:

ADD AL, [NUM1] 
DAA
ADD [SUM], AL  

It does first ordinary binary ADD with the BCD values. If the second input was "18", then add 18h, 34h happens and AL = 4Ch, which is illegal BCD value, because first digit is 4 (OK), and second digit is 12 (illegal).

Then next instruction DAA "Decimal adjust AL after Addition" will fix the result back to valid BCD, i.e. AL = 52h (decimal 82, binary 0101 0010) , where first BCD digit is 5, and second is 2. And in human decimal way, that's what you want, because 18+34 = 52.

And finally the resulting BCD sum is added to uninitialized memory SUM by ADD [SUM], AL, technically another bug, although under normal circumstances that memory contains zero value, so it works by accident (did I wrote you already, that you must be precise in assembly?).

Then few instructions later you have:

MOV BL, 10000B      ; mov bl,16
DIV BL

That's second dead kitten, you are a monster. Actually div by 16 counts like two dead kittens (and I'm generous, as the DIV is about 5-10 times slower than MUL, not only two times slower), so the total is 3. The efficient way to divide unsigned integer by 16 is shr al,4.


LEA DL, AL

That's not even valid x86 instruction. Whatever assembler you are using, run away from it, fast. (I have strong suspicion this is the emu8086, which I personally find hugely atrocious, because of things like this, emu8086 will generally assemble any random text into some x86 instruction, so unless you debug your code a lot with disassembly view, you don't even know what instructions you are using).


How to extend it to 4 digits:

1) cleanup current version, remove mul/div by 16. Remove copy/paste redundant code, turn it into procedures, to be used like:

call getInputValue   ; returns two digit BCD in AL
mov  [NUM1],al       ; NUM1 from user
call getInputValue   ; returns two digit BCD in AL
mov  [NUM2],al       ; NUM2 from user

And then you need only single code to read two characters from user, and convert them to BCD. Debug it to be more correct, like don't do ADD to uninitialized memory, instead do mov [SUM],al to set it, not add.

2) 4 BCD packed digits need 16 bits of storage. Each digit is 4 bits = 4x4 = 16. (i.e. when 4 digit value "1234" is in ax, then the value of ax is 1234h = 0001 0010 0011 01002 = 4660 decimal.

So you need to change all memory reservations from bytes to words (NUM1 DW ?). Then you need to change all registers from 8 bit registers like al to 16 bit registers like ax.

Of course if you will just blindly replace al with ax in your current code, it will not work, because the mul/div will fail completely, etc... If you did proper cleanup as suggested in 1), you will have it a bit simpler, still you need to review every instruction, and fix it in 16b way, rewrite some parts of code.

For example the user input can be read in a loop, pick some spare 16b register to accumulate value, reset it to zero, and then do 4 times "read character, convert it to 0-9 value, shift current result 4 bits left, OR input value into lowest 4 bits" -> result is 4 digit packed BCD value.

There is no DAA for 16 bit ax, so you should rather sum the num1+num2 by bytes, resolving each addition by DAA, plus handling the CF when for example 0099 + 0011 = 0110. = lot of code, but that was your choice, to store values in BCD-packed way, instead of native binary.

from a head I guess the code would be like:

mov al,[NUM1]
add al,[NUM2]   ; add low 2 digits of NUM1 and NUM2
daa             ; bcd fix
mov [SUM],al    ; store low 2 digits
mov al,[NUM1+1]
adc al,0        ; add CF to upper 2 digits
daa             ; bcd fix
jc  addOverflow_needs5digits  ; when result is > 9999
add al,[NUM2+1] ; add upper two digits of NUM2
daa             ; bcd fix
mov [SUM+1],al  ; store upper 2 digits
jc  addOverflow_needs5digits  ; when result is > 9999
; all those DW accessed by AL may need "BYTE PTR" override in MASM

You definitely should do all the math on the paper first, with few example inputs (and write down all 3 formatting together, binary, hexa and decimal, try to convert them in head, you need to practice it a bit, to be able to tell how the CPU bits are used by CPU), so you can later debug the code easily and check if all bits contain result as expected (from paper).

Then after you understand the math behind, just write it directly in the code. You should have precise idea what is each instruction doing, and why you added it to code.

Then you can verify it all in debugger. Slowly, every single instruction, verify if it really does, what you need.

Also consider throwing out the whole BCD-packed scheme, and use rather native binary numbers. Then you can add any 4 digit values, even those resulting into 5 digits, because 9999+9999 = 19998 and that will fit into 16 bits (0..65535). For 5 to 9 digits you will need already 32 bits math.

Ped7g
  • 16,236
  • 3
  • 26
  • 63
  • Given your last statement in your answer: "consider throwing out the whole BCD-packed scheme, and use rather native binary numbers" Can you please explain to me how this is possible? We just started learning Assembly in my school a couple months ago so most of Assembly's concept are still blurry to me. – Abby Dec 03 '17 at 04:00
  • it does't have to do anything with assembly. It's about how numbers are encoded in computers. In "bits". Each bit can hold either 0 or 1. If you group 8 of them, you can have 2^8 = 256 different values. If you interpret those bits by binary numbers encoding (see the first link in answer), it works as integer value 0..255 (or -128..+127 when interpreted as signed value). Anyway, byte is 8 bits, and what you do with them in code gives them meaning. Some bytes are used as integers, some as strings, some as sounds, etc... Read something about basic computer architecture and learn binary numbers. – Ped7g Dec 03 '17 at 05:27
  • @Abby It's same also in other programming languages. The only small difference is, that in assembly you have very direct and simple way to manipulate bits and bytes, while in high level languages some bit operations are not as accessible, as they are hidden under high level types/objects, so in assembly these basic manipulations with bit values are simpler to do. ... but you must understand why user input "34" can be encoded in 8 bits also as `0011 0100` = packed BCD, or `0010 0010` = native binary, or anything else you design + create code for it. But `add` works best with native binary. – Ped7g Dec 03 '17 at 05:30
  • @Ped7g - it's possible but not clear that the assignment is to implement this using BCD. – rcgldr Dec 03 '17 at 10:33