2016-11-18 5 views
0

16進数で数値を出力するための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

このプログラムの出力は、私が期待したものですが、私はのASCIIコードを取得するために数字上の0x30のを追加しようとしたとき、数字は、出力は不器用だった。そこにいくつかのトリックですか、私はここでいくつかのポイントを欠場ですか?

ありがとうございます!

+1

'HEX_OUT:db '0x0000''は**文字列**です。 0 'は既にあります。 ASCII '0'に初期化された値に0x30を追加する必要はありません。 'print_hex'を複数回呼び出しても、このコードが書かれた方法では動作しないことに注意してください。' HEX_OUT'が文字列 '0x0000'に初期化されることが予想されます。 –

+0

ありがとう、ありがとう! –

+0

'add byte [bx]、cl' ..' movバイト{bx}、cl'を実行する場合、最初に '' 0'''または '' A'-10'を追加する必要があります。 – Ped7g

答えて

1

あなたの元の質問への答え:

あなたがバッファに数字を書くことadd byte [bx], clを行うには、最初の時間は、それが正常に動作しますので、バッファはすでに、'0'が含まれているため。 print_hexをもう一度呼び出すと、HEX_OUTの内容が既に変更されているので、もうちょっと変わってしまいます(トリビア:最初に印刷された16進数では、2番目の値も正しく印刷されます)。


は今ちょうど楽しみのために私は Iはおそらく自分のために print_hexを行うだろうか追加しています。 x86 ASMプログラミングのための追加のアイデアを提供するかもしれないが、なぜ私がそれらをやっているのかを説明するために多くのコメントをしようとした。

まずは書式設定機能を分離するだろう。最終的には他の場所でそれを再利用することができるので、入力は数値バッファポインタとターゲットバッファポインタの両方です。私は、ASCII変換のためのLUT(ルックアップテーブル)を使用しています。サイズについて気にするならば、より少ないバイトで分岐するコードでそれを行うことが可能であり、より遅い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 

そして最後にブートコードから使用例:

mov  dx,0x1fb6  ; The hexadecimal to be printed 
    call print_hex 
    mov  dx,ax   ; works also when called second time 
    call print_hex  ; (but would be nicer to print some space between them) 

    jmp  $    ; loop infinitely 

(私はこのコードをいくつか拡張して(コンパイルして実行する)、別の部分と32b環境(32bにするためにいくつかの行をパッチする)でしかないことを確認しました。いくつかのバグが入っている可能性があります。私は完全なブートコードとして検証するために16b環境を持っていません。)

関連する問題