问题描述
我一直在关注如何编写操作系统的讲义,并且一直在掌握汇编语言,尤其是NASM。 (在这里讲课,感兴趣:)
首先要完成的任务之一是编写一个程序,该程序将在屏幕上打印一个16位十六进制数字的ASCII表示。
在下面的程序中,测试编号是'0x6bf1'。程序打印数字,但是十六进制数字反转,即'01fb6'。我无法弄清楚为什么 - 有人能给我一个提示吗? (这不是家庭作业btw)。
[org 0x7c00]; BIOS将bootloader加载到地址0x7c00
mov dx,0x6bf1
call print_hex
jmp $;打印结果后挂起
print_hex:
mov cl,0
mov bx,HEX_OUT
add bx,2;在'0x'之后开始写
循环:
cmp cl,16
je终于
mov ax,dx
shr ax, cl
和ax,0x000f
add ax,0x30
cmp ax,0x39
jg add_7
mov byte [bx],al
add bx,1 ;增加下一轮的写入地址
add cl,4;下一轮增量位移
jmp循环
add_7:;处理字母(A-F)
add ax,0x07
mov byte [bx],al
add bx,1;增加下一轮的写入地址
add cl,4;下一轮增量位移
jmp循环
终于:
mov bx,HEX_OUT
调用print_string
ret
print_string:
mov ah,0x0e;设置为BIOS Teletype例程
mov dx,bx
print_loop:
mov cl,[bx]
cmp cl,0
je退出
mov al,[bx]
int 0x10
add bx,1
jmp print_loop
出口:
ret
HEX_OUT:db'0x0000',0
; padding and magic BIOS number
times 510 - ($ - $$)db 0
dw 0xaa55
感谢和你已经知道你的代码出了什么问题。解决方案是有一个循环:
mov cl,12
Loop:
mov ax, dx
shr ax,cl
...
sub cl,4
jnb Loop
由于这不是作业,我们可以进一步学习,并学会编写更好的代码。主循环的主体效率低下,因为您在没有必要的情况下重复执行多条指令。
mov cl,12
循环:
mov ax,dx
shr ax,cl
和al,0x0F
add al,0
cmp al,9
jng isDigit
add al,7;处理字母(A-F)
isDigit:
mov byte [bx],al
inc bx;增加下一轮的写地址
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;设置BIOS BIOS Teletype Routine - 函数编号
mov bh,0;设置为BIOS Teletype例程 - 显示页面
jmp TestEnd
print_loop:
int 0x10
inc si
TestEnd:
mov al,[si]
cmp al,0
jne print_loop
ret
I've been following lecture notes on how to write an operating system and have been getting to grips with assembly language, specifically NASM. (Lecture notes here, for interest: https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf)
One of the first tasks is to write a program that will print to the screen an ASCII representation of a 16-bit hexadecimal number.
In the program below, the test number is '0x6bf1'. The program prints the number, but with the hex digits reversed, i.e. '01fb6'. I cannot figure out why - would someone be able to give me a hint? (This isn't homework 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
Thanks to the comments by Michael Petch and Margaret Bloom you already know what was wrong with your code. The solution is to have a loop like:
mov cl, 12
Loop:
mov ax, dx
shr ax, cl
...
sub cl, 4
jnb Loop
Since this isn't homework we can go a bit further and learn to write better code. The body of the main loop is inefficient because you repeat a number of instructions where this isn't necessary. See how much smaller the code gets by just jumping over the addition with 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
Please note that I used AL
instead of AX
. The upper byte of AX
at these points in the program has no relevant content. Furthermore using AL
reduces the code size. Using inc bx
rather than add bx,1
also reduces code size.
The BIOS Teletype function has BH
as an argument, so best not use BX
to iterate over the string when outputting the result.
Why do you load the character 2 times (once in CL
, once in AL
)?
See how I've put the test for the end of the string near the end of the loop? This saves unnecessary jumping around. In future programs that you'll write this will become an important detail.
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
这篇关于为什么这个汇编语言程序以相反的顺序打印十六进制数字?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!