2016-11-18 9 views
0

Я писал программу сборки x86 для вывода числа в шестнадцатеричном формате. Программа была собрана с использованием nasm и файла изображения, запускаемого qemu. Поведение программы сильно смутило меня. Как показывает рабочая программа ниже, мне не нужно будет добавлять 0x30 в цифру, чтобы заставить ее печатать символ этой цифры.x86 Сводный номер буфера строки в ASCII

; Boot sector code offset: 0x7c00 
[org 0x7c00] 

    mov dx, 0x1fb6 ; The hexadecimal to be printed 
    call print_hex ; call the function 
    jmp $ ; jump infinitely 

%include "print_string.asm" ; Include the print_string function 

print_hex: 
    pusha ; push all registers to stack 
    mov ax, 0x4 ; rotate through the number four times 
print_hex_loop: 
    cmp ax, 0x0 ; compare the counter with 0 
    jle print_hex_end ; if it is zero then jump to the end 
    mov cx, dx ; move dx to cx 
    and cx, 0x000F ; take the lower four binary digits of cx 
    cmp cx, 0xa ;compare the digits with 0xa 
    jge print_hex_letter ; if it is larger than a, jump to printing character 
    add cx, 0x0 ; otherwise print the ascii of a number 
    jmp print_hex_modify_string ; jump to routine for modifing the template 
print_hex_letter: 
    add cx, 0x7 ; print the ascii of a letter 
print_hex_modify_string: 
    mov bx, HEX_OUT ; bring the address of HEX_OUT into dx 
    add bx, 0x1 ; skip the 0x 
    add bx, ax ; add the bias 
    add byte [bx], cl ; move the character into its position 
    shr dx, 4 ; shift right 4 bits 
    sub ax, 0x1 ; subtract 1 from the counter 
    jmp print_hex_loop ; jump back to the start of the function 
print_hex_end: 
    mov bx, HEX_OUT ; move the address of HEX_OUT to bx 
    call print_string ; call the function print_string 
    popa ; pop all registers from stack 
    ret ; return to calling function 

HEX_OUT: 
    db '0x0000',0 ; The template string for printing 

    times 510-($-$$) db 0 ; fill zeros 
    dw 0xaa55 ; MAGIC_FLAG for boot 

boot_sect.asm

print_string: 
    pusha 
    mov ah, 0x0e 
    mov al, [bx] 
print_string_loop: 
    cmp al, 0x0 
    je print_string_end 
    int 0x10 
    add bx, 0x1 
    mov al, [bx] 
    jmp print_string_loop 
print_string_end: 
    popa 
    ret 

print_string.asm

Вывод этой программы является то, что я ожидал, но когда я попытался добавить 0x30 на цифры, чтобы получить ASCII код цифры, результат был тарабарщин. Есть ли какой-то трюк или я не хватает некоторых ключевых моментов здесь?

Спасибо!

+1

Поскольку 'HEX_OUT: db '0x0000'' является ** строкой **, которая представляет символы ASCII для' 0 'уже. Не нужно добавлять 0x30 к значениям, которые были инициализированы ASCII '0'. Также обратите внимание, что вызов 'print_hex' более одного раза не будет работать, потому что, как этот код был написан, ожидалось, что' HEX_OUT' инициализируется строкой '0x0000'. –

+0

OK Я вижу, спасибо! –

+0

Из-за 'add byte [bx], cl' .., если вы сделаете' mov byte [bx], cl', вам придется сначала добавить '' 0'' или '' A'-10' '. – Ped7g

ответ

1

Ответ на ваш первоначальный вопрос:

Поскольку вы add byte [bx], cl писать цифры в буфер, а буфер уже содержит '0', поэтому первый раз он будет работать правильно. Вызов print_hex второй раз приведет к появлению тарабарщины снова, так как содержимое HEX_OUT уже изменено (мелочи: какой шестнадцатеричный номер, напечатанный как первый, позволит также напечатать некоторое второе значение?).


Теперь просто для удовольствия, я добавляю, как я, вероятно, сделать print_hex для себя. Возможно, это даст вам дополнительные идеи для программирования x86 ASM, я попытался прокомментировать это, чтобы объяснить, почему я делаю то, что я делаю:

Сначала я бы выделил функцию форматирования, поэтому я может в конечном итоге повторно использовать его в другом месте, поэтому ввод - это как указатель числа, так и указатель целевого буфера. Я использую LUT (look up table) для преобразования ASCII, поскольку код проще. Если вы заботитесь о размере, это можно сделать в коде с ветвлением в меньшем количестве байтов и использовать медленнее pusha/popa для сохранения регистров.

format_hex: 
    ; dx = number, di = 4B output buffer for "%04X" format of number. 
    push bx    ; used as temporary to calculate digits ASCII 
    push si    ; used as pointer to buffer for writing chars 
    push dx 
    lea  si,[di+4]  ; buffer.end() pointer 
format_hex_loop:  
    mov  bx,dx   ; bx = temporary to extract single digit 
    dec  si    ; si = where to write next digit 
    and  bx,0x000F  ; separate last digit (needs whole bx for LUT indexing) 
    shr  dx,4   ; shift original number one hex-digit (4 bits) to right 
    mov  bl,[format_hex_ascii_lut+bx] ; convert digit 0-15 value to ASCII 
    mov  [si],bl   ; write it into buffer 
    cmp  di,si   ; compare buffer.begin() with pointer-to-write 
    jb  format_hex_loop ; loop till first digit was written 
    pop  dx    ; restore original values of all modified regs 
    pop  si 
    pop  bx 
    ret 
format_hex_ascii_lut:  ; LUT for 0-15 to ASCII conversion 
    db  'ABCDEF' 

Тогда для удобства print_hex функции могут быть добавлены также, предоставляя свой собственный буфер для форматирования с «0x» и NUL терминатора:

print_hex: 
    ; dx = number to print 
    push di 
    push bx 
    ; format the number 
    mov  di,HEX_OUT+2 
    call format_hex 
    ; print the result to screen 
    lea  bx,[di-2]  ; bx = HEX_OUT 
    ; HEX_OUT was already set with "0x" and nul-terminator, otherwise I would do: 
    ; mov  word [bx],'0x' 
    ; mov  byte [bx+6],0 
    call print_string 
    pop  bx 
    pop  di 
    ret 
HEX_OUT: 
    db  '0x1234',0  ; The template string for printing 

И наконец пример использования из кода загрузки:

(Я проверил этот код в некоторой степени (что он будет компилировать и запускать), хотя только отдельными его частями и в среде 32b (исправление нескольких строк, чтобы сделать его 32b), поэтому некоторые ошибки могут быть пропущены. У меня нет среды 16b, чтобы проверить ее как полный загрузочный код.)