Что не так с этой функцией (написанной на ассемблере) для печати шестнадцатеричных чисел? - PullRequest
0 голосов
/ 31 марта 2020

Я только новичок в сборке и читал книгу ОС Ника Бланделла, когда натолкнулся на вопрос о написании функции, которая могла бы печатать шестнадцатеричное число. Но, несмотря на многократную проверку логики c, я не могу понять, почему этот код не работает. Пожалуйста, помогите, я был бы признателен.


HEX_OUT:    db  '0x0000', 0
MASK:       dw  0b1111000000000000
COUNTER:    db  3

print_string :

    pusha                               ;SAVES ALL REGISTER VALUES TO BE RESTORED WHEN RETURNING.
    mov ah, 0x0e

    jmp print_loop                      ;NOT COMPULSORY

    print_loop :
        mov al, [bx]
        add bx, 1                       ;ADD 1, NOT 8, NOT 16.
        int 0x10
        cmp al, 0                       ;SETS A FLAG ACCORDING TO RESULT OF COMPARISON.
        jne print_loop                  ;CAUSES LOOP.
        jmp final_block                 ;CAN BE REPLACED BY THE STATEMENTS IN final_block, NO NEED FOR MAKING NEW LABEL.

    final_block :
        popa
        ret                             ;RETURNS TO THE POINT WHERE CALL HAPPENED.

print_hex :
    pusha

    mov bx, HEX_OUT
    add bx, 2

    alter_loop :                        ;LOOP TO ALTER HEX_OUT
        mov ax, [MASK]
        cmp ax, 0                       ;CONDITION TO END LOOP
        je after_loop

        mov ax, dx                      ;GETTING(INTO AX) THE DATA FOR N-TH POSITION 
        and ax, [MASK]

        mov cx, [COUNTER]

        shift_loop :
            cmp cx, 0
            je end_shift_loop
            shr ax, 4
            sub cx, 1
        end_shift_loop:

        cmp ax, 0x0009                       ;DO HEX->ALPHABET IF NUMBER IS GREATER THAN 9
        jle skip_hex_to_alphabet

        add ax, 39                       ;EQUIVALENT TO (sub ax, 48--- sub ax, 9 ---add ax, 96)

        skip_hex_to_alphabet :

        add ax, 48                      ;ADDING 48(ASCII OF 0), IS ALREADY SUBTRACTED IF N-TH NUMBER>9

        mov [bx], al                    ;STORING DATA IN LOCATION POINTED TO BY BX
        add bx, 1                       ;INCREMENT FOR LOOP
        mov ax, [MASK]                  ;CHANGING MASK
        shr ax, 4
        mov [MASK], ax

        mov ax, [COUNTER]               ;UPDATING COUNTER
        sub ax, 1
        mov [COUNTER], ax

        jmp alter_loop

    after_loop :
    mov bx, HEX_OUT
    call print_string    
    popa
    ret  

После вызова функции, как: -

mov dx, 0x1fd6
call print_hex

Она печатает, 0xWGd0 вместо 0x1fd6.

1 Ответ

2 голосов
/ 31 марта 2020

Вы пропускаете прыжок назад к shift_loop и неправильно указали размер COUNTER.

Поскольку вы используете mov cx, [COUNTER], COUNTER должно быть словом, исправьте его:

COUNTER:    dw  3

Наконец, вы не корректно сдвигаете маскированные значения. На первой итерации and ax, [MASK] производит 0x1000, а в shift_loop это уменьшается до 0x0100, потому что он повторяется только один раз.
Закройте l oop прыжком:

    shift_loop :
        cmp cx, 0
        je end_shift_loop
        shr ax, 4
        sub cx, 1
    jmp shift_loop

    end_shift_loop:

Мои два цента: я пишу и читаю ассемблер более двух десятилетий, и ваш код сумел сбить меня с толку. Я не ожидал, что процедура шестнадцатеричной печати будет l oop по маске stati c и сохраню результат в строке stati c. Это слишком запутанно для поставленной задачи.
Вы можете просто извлечь кусочки с переменным счетчиком сдвига, уменьшенным на четыре, и (постоянной) маской. Затем вы можете даже использовать 16-байтовую таблицу поиска, чтобы преобразовать полубайт в символ, избегая ветвления.

Кроме того, поскольку вы программируете для DOS, очень стоит найти копию TASM в Интернете и использовать ее отладчик (td - Turbo Debugger). Легко использовать неправильный размер для переменной и работать с мусором, отладчик немедленно покажет вам это.


Если вам нравится конкретный пример, вот простая реализация.

;AX = number to print in hex
hex:
  mov cx, 12              ;Shift counter, we start isolating the higher nibble (which starts at 12)
  mov bx, hexDigits       ;Lookup tables for the digitals
  mov dx, ax              ;We need a copy of the number and AX is used by the int10 service

.extract:
  mov si, dx              ;Make a copy of the original number so we don't lose it. Also we need it in SI for addressing purpose
  shr si, cl              ;Isolate a nibble by bringing it at the lower position
  and si, 0fh             ;Isolate the nibble by masking off any higher nibble

  mov al, [bx + si]       ;Transform the nibble into a digit (that's why we needed it in SI)
  mov ah, 0eh             ;You can also lift this out of the loop. It put it here for readability.
  int 10h                 ;Print it

  sub cx, 4               ;Next nibble is 4 bits apart
jnc .extract              ;Keep looping until we go from 0000h to 0fffch. This will set the CF

  ret

hexDigits db "0123456789abcdef"
...