Добавление двух двоично-десятичных чисел-мипов

Я пытаюсь добавить 2 числа, которые хранятся в 2 регистрах. каждое число находится в формате bcd и состоит из 8 цифр. Мне интересно, есть ли у меня лучший способ, чем просто работать с каждыми 4 битами за раз.

Вот что я начал:

.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

person sara    schedule 04.12.2017    source источник


Ответы (2)


На соответствующей странице Википедии есть алгоритм добавления упакованного BCD:

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
}

Это должно быть прямолинейно для перевода на сборку MIPS.

person harold    schedule 04.12.2017
comment
Я не пишу код на C. Можете ли вы помочь мне преобразовать t2 = ~ t2 & 0x11111110; t2 = (t2 ›› 2) | (t2 ›› 3); в мипс или java/С#? Спасибо - person sara; 04.12.2017
comment
@sara, на C# было бы точно так же. В Java это будет >>> вместо >>, поскольку в java подписанность кодируется в операции, а не в типе. - person harold; 04.12.2017
comment
Понятно!!! есть ли хороший способ проверить, есть ли у суммы двух чисел bcd ручная кладь? - person sara; 05.12.2017
comment
@Сара, я не знаю. Должно быть возможно как-то - person harold; 05.12.2017
comment
@sara Возможно, я ошибаюсь, но я думаю, что если сумма меньше любого из аргументов, она переполнится. (т.е. 99+99 = [1]98 -> 98 ‹ 99, 00+00 = 00 -> 00 ›= 00, 00+50 = 50 -> 50 ›= {00, 50} и т. д.) ( вы можете в основном игнорировать, что значения являются BCD, он должен работать как для BCD, так и для двоичных значений, поэтому вы также можете использовать простое двоичное сравнение со значениями BCD) - person Ped7g; 05.12.2017
comment
@ Ped7g, это сработает, но этот код из Википедии иногда оставляет верхнюю цифру в плохом состоянии (больше 9). Если бы это тоже было исправлено, то сработал бы обычный прием меньшего ввода. Я не смог найти быстрый способ исправить эту верхнюю цифру, просто проверьте ее грубой силой и добавьте 6, если она слишком высока (можно сделать без ответвлений, но это все еще выглядит как хак).. может быть, у вас есть лучший идея есть - person harold; 05.12.2017
comment
О, я не проверял, что алгоритм вики оставляет в верхнем байте. Поскольку мое предложение уже похоже на разветвление, некоторые ответвления, вероятно, приемлемы, проверка переполнения всегда является дорогостоящей. Но вы можете сделать +6 только тогда, когда он уже недействителен, когда он действителен, вы создадите новую проблему, поэтому теперь у нас есть две ветви... но если верхняя цифра недействительна, то, очевидно, произошло переполнение, поэтому проверка может выглядеть как overflow = (!sum.top_digit.isValid) || (sum < a); два сравнения + ветвь. - person Ped7g; 05.12.2017
comment
@ Ped7g Как мне это сделать: !sum.top_digit.isValid в mips? - person sara; 05.12.2017
comment
@sara invalid_top_digit = (sum >= 0xA0000000) т.е. li $<temp_reg>,0xA0000000 sltu $<test_reg>, $<sum_reg>, $<temp_reg> beq $<test_reg>, $zero, top_digit_is_invalid (test_reg может быть таким же, как temp_reg, т.е. требуется только один запасной временный регистр, но не используйте $at, потому что li большой константы может потребоваться составить li из 2 инструкций с $at как временное) .. идея теста, надеюсь, очевидна? (почему все недопустимые старшие цифры ›= 0xA00...) - person Ped7g; 05.12.2017
comment
@ Ped7g Я думаю, что у меня неправильный расчет. Я думаю, что сделал != неправильно, я использовал логику, не так ли? Это мой код: li $t1,0 li $t2,0 sltu $t2, $s4, $s1 li $t3,0 li $t4,0 #invalid_top_digit = (сумма ›= 0xA0000000), т.е. li $t3,0xA0000000 sltu $t4, $s4, $t3#$t4=top_digit_is_invalid li $t3, -1 # загрузить -1 в хелп-регистр xor $t4, $t4, $t3 # фактическая не-операция или $s5, $t4,$t2 #overflow = (!sum.top_digit.isValid) || (сумма ‹ а); - person sara; 05.12.2017
comment
@sara 0 xor -1 = -1 и 1 xor -1 = -2 ... Вы написали побитовое не, а нужно логическое не (0 => 1, 1 => 0), т.е. xor с 1. Но, может быть, просто переверните ветвь, чтобы проверить на ноль вместо 1, нет необходимости переворачивать значение результата, если в этом нет крайней необходимости. Кроме того, если это MARS или SPIM, у вас есть отладчик, так что вы можете сами проверить, какие значения находятся в регистрах и как они развиваются в ваших вычислениях. А с A || B вы можете использовать стратегию раннего завершения, оценивать только более простое из A или B и делать другое только тогда, когда первое было ложным. Если первое истинно, вы можете сообщить об истинности. - person Ped7g; 05.12.2017
comment
@ Ped7g Вот что я сделал, я проверил это, и, похоже, это работает. Как я могу быть уверен, что это правильно? Есть ли симулятор mips, который показывает значения регистров в bcd? li $t1,0 li $t2,0 sltu $t2, $s4, $s1 li $t3,0 li $t4,0 #invalid_top_digit = (sum ›= 0xA0000000) т.е. li $t3,0xA0000000 sltu $t4, $s4 , $t3#$t4=top_digit_is_invalid li $t3, 1 # загрузить 1 в регистр справки xor $t4, $t4, $t3 # фактическая не-операция или $s5, $t4,$t2#overflow = (!sum. top_digit.isValid) || (сумма ‹ а); - person sara; 05.12.2017
comment
@sara Значения, показанные в шестнадцатеричном формате, хорошо работают для отображения BCD ... т. Е. Двоичный 0x1234 представляет десятичное значение 1234 в формате BCD. В шестнадцатеричном формате 1 цифра (0..F) = 4 бита. В BCD 4 бита = 1 цифра (0..9) => т. е. BCD похож на шестнадцатеричный, но не использует A..F цифры, поэтому вы не можете напрямую складывать два значения по двоичному add. Я предполагаю, что ваш симулятор по умолчанию показывает значения регистров в шестнадцатеричном формате, поэтому он должен быть легко читаемым? - person Ped7g; 05.12.2017
comment
@ Ped7g Я использую симулятор Марса, он может показать его в шестнадцатеричном формате. Это хорошая идея - смотреть на это так. - person sara; 05.12.2017

Основываясь на ответе @harold и помощи @Ped7g, я написал алгоритм добавления BCD в mips. Я добавил фрагмент кода, чтобы проверить, есть ли у нас переполнение, и справиться с ним. Теперь это работает для всех случаев.

#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:
person sara    schedule 17.01.2018
comment
как работает addi $s2,$zero,0x56251894? Иммедиаты в MIPS имеют только 16 бит, поэтому вам придется использовать lui с ori или addi или просто использовать псевдоинструкцию move, чтобы позволить ассемблеру решить, как загрузить значение. - person phuclv; 18.01.2018
comment
@ LưuVĩnhPhúc - Работает. Вы можете запустить его в симуляторе mips. - person sara; 18.01.2018