2

I'm trying to add 2 numbers that are stored in 2 registers. each number is in bcd format and has 8 digits. I'm wondering if I have a better way then just work on every 4 bits at a time.

This is what I started:

.text

main:
    addi $s2,$zero,00010010001101000101011001111000#num1
    addi $s3,$zero,00010100011110000101001000110110#num2

    addi $t0,$zero,00000000000000000000000000001111#mask

    and $t1,$t0,$s2#geting digit#1 of num1
    and $t2,$t0,$s3#geting digit#2 of num2

    add $t3,$t1,$t2#adding digits
    #checking for overflow
    #doing the same for the rest of the digits



    #add $s4,$s3,$s2
sara
  • 129
  • 1
  • 9

2 Answers2

2

The relevant Wikipedia page has an algorithm for packed BCD addition:

uint32_t BCDadd(uint32_t a,uint32_t b)
{
    uint32_t  t1, t2;    // unsigned 32-bit intermediate values

    t1 = a + 0x06666666;
    t2 = t1 ^ b;                   // sum without carry propagation
    t1 = t1 + b;                   // provisional sum
    t2 = t1 ^ t2;                  // all the binary carry bits
    t2 = ~t2 & 0x11111110;         // just the BCD carry bits
    t2 = (t2 >> 2) | (t2 >> 3);    // correction
    return t1 - t2;                // corrected BCD sum
}

This should be straight-forward to translate into MIPS assembly.

phuclv
  • 37,963
  • 15
  • 156
  • 475
harold
  • 61,398
  • 6
  • 86
  • 164
  • I don't code in C . By any chance can you help me converting t2 = ~t2 & 0x11111110; t2 = (t2 >> 2) | (t2 >> 3); to mips or java/c#? Thank's – sara Dec 04 '17 at 20:09
  • @sara it would be exactly the same in C#. In Java, it would be `>>>` instead of `>>`, since in java signedness is encoded in the operation rather than the type. – harold Dec 04 '17 at 20:15
  • Got it!!! is there any good way to check if the sum of the 2 bcd numbers have a carry-on? – sara Dec 04 '17 at 21:11
  • @sara I don't know. It should be possible somehow – harold Dec 04 '17 at 21:44
  • 1
    @sara I may be wrong, but I think if the sum is less-than any of the arguments, it did overflow. (i.e. 99+99 = [1]98 -> 98 < 99, 00+00 = 00 -> 00 >= 00, 00+50 = 50 -> 50 >= {00, 50}, etc...) (you can basically ignore that the values are BCD, it should work both for BCD and binary values, so you can use simple binary compare with BCD values too) – Ped7g Dec 05 '17 at 13:15
  • 2
    @Ped7g that would work, but that code from wikipedia sometimes leaves the upper digit in a bad state (greater than 9). If it fixed that too then the usual "less than input" trick would work. I couldn't find a quick way to fix that upper digit though, just pretty much brute-force check it and add 6 if it's too high (can be done branchless but it still looks like a hack).. maybe you have a better idea there – harold Dec 05 '17 at 13:27
  • 2
    Oh, I didn't check what the wiki algorithm leaves in upper byte. As my suggestion is already branching-like, some branch is probably acceptable, the overflow check is always costly. But you can do +6 only when it is already invalid, when valid you would introduce new problem, so we have now two branches... but if the top digit is invalid, then the overflow "obviously happened", so the check can go like `overflow = (!sum.top_digit.isValid) || (sum < a);` two compares + branch. – Ped7g Dec 05 '17 at 13:50
  • @Ped7g How would I do this: !sum.top_digit.isValid in mips? – sara Dec 05 '17 at 14:45
  • 1
    @sara `invalid_top_digit = (sum >= 0xA0000000)` i.e. `li $,0xA0000000` `sltu $, $, $` `beq $, $zero, top_digit_is_invalid` (test_reg can be same as temp_reg, i.e. just one spare temporary register needed, but don't use `$at`, because the `li` of big constant may need to compose the `li` from 2 instructions with `$at` as temporary) .. the idea of the test is hopefully obvious? (why all invalid top digits are >= 0xA00...) – Ped7g Dec 05 '17 at 14:56
  • @Ped7g I think Im getting a wrong calculation.I think I did the != not correct,I used the logical not is that correct? This is my code: li $t1,0 li $t2,0 sltu $t2, $s4, $s1 li $t3,0 li $t4,0 #invalid_top_digit = (sum >= 0xA0000000) i.e. li $t3,0xA0000000 sltu $t4, $s4, $t3#$t4=top_digit_is_invalid li $t3, -1 # load -1 into help-register xor $t4, $t4, $t3 # actual not-operation or $s5, $t4,$t2#overflow = (!sum.top_digit.isValid) || (sum < a); – sara Dec 05 '17 at 15:17
  • 1
    @sara 0 xor -1 = -1 and 1 xor -1 = -2 ... You have written bitwise not, but you need logical not (0 => 1, 1 => 0), i.e. `xor` with `1`. But maybe just flip the branch to test for zero instead of 1, no need to flip the result value unless absolutely necessary. Also if this is MARS or SPIM, you have debugger, so you can check yourself, what values are happening to be in registers and how your calculation evolves them. And with `A || B` you may use terminate-early-strategy, evaluate just the simpler of A or B, and do the other only when the first was false. If first is true, you can report true. – Ped7g Dec 05 '17 at 15:22
  • @Ped7g So this is what I did I tested it and it seems to work. How would I be sure its correct?Is there a mips simulator that shows registers values in bcd? li $t1,0 li $t2,0 sltu $t2, $s4, $s1 li $t3,0 li $t4,0 #invalid_top_digit = (sum >= 0xA0000000) i.e. li $t3,0xA0000000 sltu $t4, $s4, $t3#$t4=top_digit_is_invalid li $t3, 1 # load 1 into help-register xor $t4, $t4, $t3 # actual not-operation or $s5, $t4,$t2#overflow = (!sum.top_digit.isValid) || (sum < a); – sara Dec 05 '17 at 16:03
  • 1
    @sara Values shown in hexa works well as BCD display ... i.e. binary `0x1234` is representing value `1234` decimal in BCD-packed way. In hexadecimal 1 digit (0..F) = 4 bits. In BCD 4 bits = 1 digit (0..9) => i.e. BCD is like hexa, but doesn't use `A..F` digits, so you can not add two values by binary `add` directly. I guess your simulator does show register values in hexadecimal formatting by default, so it should be easily readable? – Ped7g Dec 05 '17 at 16:21
  • @Ped7g I Am using mars simulator it can show it in hexadecimal.It's a good idea looking on it like this. – sara Dec 05 '17 at 16:25
0

Based on @harold answer and the help of @Ped7g I have written the algorithm of BCD addition in mips. I added a piece of code to check if we have overflow and deal with it. Now it works for all cases.

#Description: program to sum 2 numbers in BCD format.
#The numbers must be in $s2, $s3.
#The answer will appear in $s4. If we have overflow $s5 = 1 else $s5=0;
#The program in c: 
#Based on an algorithm to calculate an unsigned 8-digit packed BCD add using 32-bit binary operations.

#   uint32_t  t1, t2;               // unsigned 32-bit intermediate values
#   t1 = a + 0x06666666;
#   t2 = t1 ^ b;                   // sum without carry propagation
#   t1 = t1 + b;                   // provisional sum
#   t2 = t1 ^ t2;                  // all the binary carry bits
#   t2 = ~t2 & 0x11111110;         // just the BCD carry bits
#   t2 = (t2 >> 2) | (t2 >> 3);    // correction
#   uint32_t  sum = t1 - t2;       // corrected BCD sum
#   uint32_t overflow = (!sum.top_digit.isValid) || (sum < a);
#   //if we have overflow it means that the MSB is not in valid formt.
#   sum = (overflow  == 1 ? sum - 0xA0000000 : sum); in this case  we must sub 0xA from MSB

.text
main:
    #assigning 2 numbers in bcd format to add them up
    addi $s2,$zero,0x56251894
    addi $s3,$zero,0x99649449

    addi $t1,$s2,0x06666666 #t1 = a + 0x06666666;
    xor $t2,$t1,$s3     #t2 = t1 ^ b;   // sum without carry propagation
    addu $t1,$t1,$s3    #t1 = t1 + b;   // provisional sum
    xor $t2,$t1,$t2     #t2 = t1 ^ t2;  // all the binary carry bits

    add $t3,$zero,-1        #load -1 into help-register
    xor $t2, $t2,$t3    #actual not-operation
    add $t4,$zero,0x11111110#loading 0x11111110 into help-register
    and $t2,$t2,$t4     #t2 = ~t2 & 0x11111110;//just the BCD carry bits

    add $t3,$zero,$zero #reseting $t3
    add $t4,$zero,$zero #reseting $t4

    srl $t3,$t2,2       #(t2 >> 2)
    srl $t4,$t2,3       #(t2 >> 3)
    or $t2,$t3,$t4      #(t2 >> 2) | (t2 >> 3)
    sub $s4,$t1,$t2     #t2 = (t2 >> 2) | (t2 >> 3)// correction

    add $t1,$zero,$zero #reseting $t1

    add $t2,$zero,0xA0000000#load 0xA0000000 into help-register
    sltu $t1,$s4,$t2

    # checking if a>0x50000000 && $s4<0x40000000 
    #This is to make sure that if MSB>f then we can control it(We will use slt instead of sltu on ).
    add $t5,$zero,0x50000000#load 0x50000000 into help-register
    sltu $t6,$s2,$t5
    bnez $t6,isValid    #checking if a>0x50000000
    add $t5,$zero,0x40000000#load 0x40000000 into help-register
    sltu $t6,$s4,$t5
    beqz $t6,isValid    #checking if $s4<0x40000000 

    # if a>0x50000000 && $s4<0x40000000 
    addi $t2,$zero,0xA0000000#load 0xA0000000 into help-register
    slt $t1,$s4,$t2
isValid:
    #this is to check if top digit is valid 
    add $t2,$zero,1         #load 1 into help-register
    xor $t1,$t1,$t2     #actual not-operation
    or $s5,$t1,$zero    #overflow = (!sum.top_digit.isValid) || (sum < a);

    #Checking if MSB is valid if not we need to sub 1001 from this bit(MSB bit).
    beq $s5,$zero,end   #Checking if we don't have overflow
    subiu $s4,$s4,0xA0000000#if we have ofverflow we need to sub 10 from the MSB.
end:
sara
  • 129
  • 1
  • 9
  • how does `addi $s2,$zero,0x56251894` work? Immediates in MIPS are only 16 bits, so you'll have to use `lui` with `ori` or `addi`, or just use the `move` pseudoinstruction to let the assembler decide how to load the value – phuclv Jan 18 '18 at 01:39
  • @ LưuVĩnhPhúc -It works. You could run it in mips simulator. – sara Jan 18 '18 at 12:29