为什么这个汇编语言程序以相反的顺序打印十六进制数字?

时间:2016-06-18 15:35:34

标签: assembly hex nasm x86-16

我一直在关注如何编写操作系统的讲义,并且已经掌握了汇编语言,特别是NASM。 (这里的讲义,感兴趣:https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf

首要任务之一是编写一个程序,在屏幕上打印一个16位十六进制数字的ASCII表示。

在下面的程序中,测试编号为“0x6bf1”。程序打印数字,但十六进制数字反转,即'01fb6'。我无法弄清楚为什么 - 有人能给我一个提示吗? (这不是家庭作业btw)。

[org 0x7c00]            ; BIOS loads bootloader to address 0x7c00

mov dx, 0x6bf1
call print_hex
jmp $                    ; Hang after printing result

print_hex:

mov cl, 0
mov bx, HEX_OUT
add bx, 2            ; To start writing after '0x'

loop:

cmp cl, 16
je finally
mov ax, dx
shr ax, cl
and ax, 0x000f
add ax, 0x30
cmp ax, 0x39
jg add_7
mov byte [bx], al
add bx, 1            ; Increment write address for the next round
add cl, 4            ; Increment bit shift for the next round
jmp loop

add_7:               ; Handles letters (A-F)

add ax, 0x07
mov byte [bx], al
add bx, 1            ; Increment write address for the next round
add cl, 4            ; Increment bit shift for the next round
jmp loop


finally:

mov bx, HEX_OUT
call print_string
ret

print_string:
mov ah, 0x0e         ; Set up for BIOS Teletype Routine
mov dx, bx

print_loop:
mov cl, [bx]
cmp cl, 0
je exit
mov al, [bx]
int 0x10
add bx, 1
jmp print_loop

exit:
ret

HEX_OUT: db '0x0000',0

; padding and magic BIOS number
times 510-($-$$) db 0
dw 0xaa55

1 个答案:

答案 0 :(得分:3)

感谢Michael PetchMargaret Bloom的评论,您已经知道代码出了什么问题。解决方案是有一个循环:

    mov  cl, 12
Loop:
    mov  ax, dx
    shr  ax, cl
    ...
    sub  cl, 4
    jnb  Loop

由于这不是作业,我们可以进一步学习编写更好的代码。主循环的主体是低效的,因为你重复了许多指令,这是不必要的。通过7:

跳过添加,看看代码有多小
    mov  cl, 12
Loop:
    mov  ax, dx
    shr  ax, cl
    and  al, 0x0F
    add  al, "0"
    cmp  al, "9"
    jng  isDigit
    add  al, 7         ; Handles letters (A-F)
isDigit:
    mov  byte [bx], al
    inc  bx            ; Increment write address for the next round
    sub  cl, 4
    jnb  Loop

请注意,我使用AL代替AX。程序中这些点的AX的高位字节没有相关内容。此外,使用AL可以减少代码大小。使用inc bx而不是add bx,1也会减少代码大小。

BIOS Teletype功能以BH为参数,因此在输出结果时最好不要使用BX来迭代字符串。
为什么要加载角色2次(一次在CL,一次在AL)? 看看我如何将测试放在循环结束附近的字符串末尾?这节省了不必要的跳跃。在将来编写的程序中,这将成为一个重要的细节。

print_string:
    mov  si, bx
    mov  ah, 0x0E      ; Set up for BIOS Teletype Routine - function number
    mov  bh, 0         ; Set up for BIOS Teletype Routine - display page
    jmp  TestEnd
print_loop:
    int  0x10
    inc  si
TestEnd:
    mov  al, [si]
    cmp  al, 0
    jne  print_loop
    ret