BCD, 8051

Тема в разделе "WASM.ELECTRONICS", создана пользователем Ra!N, 24 апр 2010.

  1. Ra!N

    Ra!N New Member

    Публикаций:
    0
    Регистрация:
    26 окт 2006
    Сообщения:
    111
    Нужно преобразовать дв. число в дв.-десятичное (неважно, пакованное или нет -- главное получить тетрады составляющих число циферок). Контроллер серии MCS51 (8051). Он работает только с байтами (складывание, умножение, деление,..), поэтому ничего лучше не придумал, как "метод двух счетчиков", когда есть два счетчика: в цикле к ним прибавляется 1, причем к первому как к обычному числу, а ко второму, как к двоично-десятичному, с коррекцией. Цикл идет до тех пор, пока первый счетчик не сравняется с исходным числом, тогда во втором будет его упакованное BCD представление.

    Но есть проблема. Опять же из-за работы только с байтами. У меня два регистра R6:R7 образуют как бы 16-битное число, работаю с отдельными регистрами:
    Код (Text):
    1.            mov DPTR,#0  ; DPTR=0 (он сост. из DPH:DPL -- ст. и младший байт)
    2.     mov R6,#0  ; R6=0
    3.     mov R7,#0  ; R7=0
    4. check:  mov A,DPL  ; A=DPL
    5.  
    6.         cjne A,31h,cycle ; тут сравнение DPTR с 2хбайтным числом по адресу 30h
    7.         mov A,DPH
    8.         cjne A,30h,cycle
    9.         sjmp stop
    10.  
    11. ; вот собсна цикл
    12. cycle:  inc DPTR  ; вот тут 8051 МОЖЕТ работать
    13.                         ; сразу 16-битным DPTR, на этом лафа кончилась
    14.         mov A,R7  ; A=R7
    15.         add A,#1  ; A++
    16.         da A        ; десятичная коррекция A
    17.         mov R7,A  ; R7=A
    18.  
    19.         mov A,R6  ; A=R6
    20.         addc A,#0  ; A+флаг переноса
    21.         da a   ; дес. уоррекция
    22.         mov R6,A  ; R6=A
    23.         sjmp check ; возврат в цикл
    24. stop:
    Интересует вопрос, можно ли так работать с дв. десятичным числом? Т. е. прибавлять к младшему байту единицу, дес. коррекция, потом к старшему прибавить влаг переноса и опять коррекция. Равносильно ли это прибавлению 1 ко всему 2хбайтовому числу 1 и дес. коррекции?
     
  2. Ra!N

    Ra!N New Member

    Публикаций:
    0
    Регистрация:
    26 окт 2006
    Сообщения:
    111
    Ой. Раньше редактирование было, сейчас кнопочку не найду...
    Поправки:
    ...двоично-десятичное число, т. е. это второй счётчик, где по окончании должен оказаться результат.
    Вторая "1" лишняя.
     
  3. S_Alex

    S_Alex Alex

    Публикаций:
    0
    Регистрация:
    27 авг 2004
    Сообщения:
    561
    Адрес:
    Ukraine
    Вот нашел код. Может поможет.
    Код (Text):
    1. ;29 Oct 00
    2. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    3. ;
    4. ;BINDECW is called to convert the unsigned integer in r5,r4 to one to
    5. ; five decimal ASCII characters and place them in the caller's 5-byte
    6. ; direct memory buffer. BINDECW calls DIV16U to do the division.
    7. ;
    8. ;call:
    9. ;       mov     r5,MSBs
    10. ;       mov     r4,LSBs
    11. ;       mov     r0,#buffer
    12. ;       call    BINDECW
    13. ;
    14. ;return:
    15. ;      
    16. ;       r0 ==> byte above last ASCII code placed in caller's buffer
    17. ;       all other registers saved
    18. ;
    19. ;examples:
    20. ;
    21. ;input:         return (buffer+):
    22. ; r5,r4         +0      +1      +2      +3      +4      r0=>
    23. ; 0,0           "0"     n.c.    n.c.    n.c.    n.c.    +1
    24. ; 0,50          "5"     "0"     n.c.    n.c.    n.c.    +2
    25. ; 1,0           "2"     "5"     "6"     n.c.    n.c.    +3
    26. ; 255,255       "6"     "5"     "5"     "3"     "5"     +5
    27. ;
    28. ;n.c. = no change
    29. ;
    30. ;Possible future improvements:
    31. ; (1) Replace DIV16U with a routine that divides 16 by 8 for faster
    32. ;     operation.
    33. ;
    34. ;Notes: Re-entrancy not supported.  counter and save_r0 would be
    35. ;       destroyed.
    36. ;
    37. ;Original author: John Veazey, Ridgecrest, CA, 29 Oct 00
    38. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    39. ;
    40. dseg
    41. counter: ds     1
    42. save_r0: ds     1
    43. cseg
    44. BINDECW:
    45. ;
    46. ;Save address of caller's buffer
    47. ;
    48.         mov     save_r0,r0
    49. ;
    50. ;Divide caller's input by 10, four times saving each remainder on the
    51. ; stack as the next BCD digit. The first remainder produced is the 1's BCD
    52. ; digit,the second the 10's, third 100's, fourth 1000's, fifth 10,000's.
    53.  
    54.         mov     counter,#4
    55. bdw22:  mov     r1,#0  
    56.         mov     r0,#10
    57.         call    DIV16U          ;r5,r4 = (r5,r4)/(r1,r0)
    58.         mov     a,r6            ;  r7,r6 = remainder
    59.         push    acc
    60.         djnz    counter,bdw22
    61.         mov     a,r4            ;Last quotient is the last remainder
    62.         push    acc
    63.  
    64. ;
    65. ;Pop the stack until the MS non-zero digit is found
    66. ;
    67.         mov     counter,#5
    68.         mov     r0,save_r0
    69. bdw44:  pop     acc
    70.         jnz     bdw60
    71.         djnz    counter,bdw44
    72.         mov     @r0,#"0"                ;Five zeros found
    73.         inc     r0
    74.         sjmp    bdw90
    75. ;
    76. ;Convert BCD to ASCII and save in caller's buffer
    77. ;
    78. bdw60:  add     a,#"0"
    79.         mov     @r0,a
    80.         inc     r0
    81. ;
    82. ;Continue loop until all BCD digits removed from stack
    83. ;
    84.         djnz    counter,bdw74
    85.         sjmp    bdw90
    86. bdw74:  pop     acc
    87.         sjmp    bdw60
    88. ;
    89. ;Return to caller
    90. ;
    91. bdw90:  ret
    92. ;end BINDECW.ASM
     
  4. Ra!N

    Ra!N New Member

    Публикаций:
    0
    Регистрация:
    26 окт 2006
    Сообщения:
    111
    S_Alex
    Это не то.

    Попробую объяснить проблему, не пребегая к микроконтроллеру, т. к. не все его знают.

    Рассмотрим два варианта:
    1) Есть 16-битное число X, которое рассматривается как упакованное двоично-десятичное (bcd) число. К нему прибавим единицу и сделаем десятичную коррекцию (на MCS-51 это команда "da", на x86 тоже есть подобная команда). Получили какой-то результат P=X+1 (все в BCD формате), напр. 7899h+1h = 789Ah = (дес. коррекция, в данном случае это прибавление 6) = 7900h.
    2) Есть два байта X и Y, которые отождествляют 16-битное число X:Y (Y -- мл. часть). Рассматриваются тоже как bcd числа. Я хочу прибавить к нему 1. Для этого я прибавляю к младшей части 1, и делаю дес. коррекцию. Затем прибавляю к старшей части флаг переноса ("addc A,#0" на MCS-51, на x86 это вроде бы "adc ax,0", только "A" -- 8битный регистр) и делаю его дес. коррекцию. Вопрос: получим ли мы такой же результат, как в первом случае?
     
  5. SII

    SII Воин против дзена

    Публикаций:
    0
    Регистрация:
    31 окт 2007
    Сообщения:
    1.483
    Адрес:
    Подмосковье
    Ra!N
    Равносильно в том смысле, что будет получен корректный результат. Более того, во всех интеловских процессорах обработка двоично-десятичных чисел возможна только по байтам независимо от разрядности процессора (если не прибегать к помощи арифметического сопроцессора, но это отдельная песня). Дело в том, что для десятичной коррекции необходимо фиксировать факт переноса между тетрадами, и для обработки чисел, превышающих байт, необходимо фиксировать такой перенос для каждой из тетрад (кроме самой старшей, из которой происходит "нормальный" перенос), ну а Интел предусмотрела лишь один флаг вспомогательного переноса что в 8-разрядных, что в 16-32-разрядных процах -- из 3-го разряда в 4-й (а в 64-разрядных АМД, а вслед за ней и Интел и вовсе убрали поддержку двоично-десятичных операций -- команды коррекции работают только в 16- и 32-разрядных режимах, но не в 64-разрядном). Вот в мэйнфреймах вообще никаких коррекций не надо: там просто есть нормальные команды для выполнения операций над двоично-десятичными числами переменной длины, расположенными в памяти...
     
  6. Black_mirror

    Black_mirror Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2002
    Сообщения:
    1.035
    Ra!N
    http://abc.vvsu.ru/Books/ebooks_iskt/%DD%EB%E5%EA%F2%F0%EE%ED%ED%FB%E5%F3%F7%E5%E1%ED%E8%EA%E8/%CC%E8%EA%F0%EE%EF%F0%EE%F6%E5%F1%F1%EE%F0%FB/%CC%E8%EA%F0%EE%EF%F0%EE%F6%E5%F1%F1%EE%F0%FB%20iu4/mk.iu4.bmstu.ru/str/coma.html
    Команда "DAA" выполняется после сложения или вычитания двух двоично-десятичных чисел (BCD - Binary Coded Decimal). Число в BCD-коде содержит в каждой тетраде (половине байта) значения от 0 до 9. После выполнения команды "DAA" содержимое аккумулятора будет содержать правильный BCD-код результата, и флаг переноса будет установлен соответствующим образом для выполнения следующей BCD-операции, например, если складываются два 16-разрядных числа (4 десятичных разряда). Команда "DAA" работает недостаточно корректно после выполнения вычитания. Микроконтроллер 8051 не даёт возможности определить отрицательное BCD-число, поэтому достаточно трудно произвести их вычитание. Эта команда не производит преобразование байта, представленного в шестнадцатеричном коде, в BCD-число.

    Насколько я понимаю, можно сложить два младших байта BCD чисел, выполнить DAA, потом сложить два старших байта с переносом, выполнить DAA и получится правильный результат.
     
  7. Ra!N

    Ra!N New Member

    Публикаций:
    0
    Регистрация:
    26 окт 2006
    Сообщения:
    111
    Ок, понятно. Всем спасибо.