2016-11-18 1 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 byte [bx], cl'을하면 먼저 '0'또는 'A'-10'을 추가해야합니다. – Ped7g

답변

1

원래의 질문에 대한 답변 :

당신이 버퍼에 자리를 작성 add byte [bx], cl을하고, 처음으로 제대로 작동하도록 버퍼가 이미 '0'가 포함되어 있기 때문에. print_hex을 두 번째로 호출하면 HEX_OUT 내용이 이미 수정되어 있기 때문에 다시 횡설수설하게됩니다 (퀴즈 : 첫 번째로 인쇄 된 16 진수 값으로 인해 일부 두 번째 값이 올바르게 인쇄 될 수 있습니까?).


지금 그냥 재미 나는 I 아마 자신을 위해 print_hex을 얼마나 추가 해요. 어쩌면 x86 ASM 프로그래밍에 대한 추가 아이디어를 얻을 수있을 것입니다. 왜 그런 식으로 일을하는지 설명하기 위해 많은 의견을 말했습니다.

먼저 서식 지정 기능을 분리 할 것이므로 결국 다른 곳에서 다시 사용할 수 있으므로 입력은 숫자와 대상 버퍼 포인터입니다. 나는 ASCII 변환을 위해 LUT (look up table)을 사용하는데, 코드가 더 간단하다. 크기를 신경 쓰면 더 적은 바이트로 분기하는 코드에서 코드를 수행하고 더 느리게 pusha/popa을 사용하여 레지스터를 저장할 수 있습니다. 부트 코드에서

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 

그리고 마지막으로 사용 예 : 다음

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 종료와 포맷에 대한 자신의 버퍼를 제공도 추가 할 수 있습니다

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 환경이 없습니다.)