我正在编写一个x86汇编程序,以十六进制输出一个数字。该程序使用nasm进行汇编,图像文件由qemu运行。程序的行为使我很困惑。正如下面的工作程序所示,我不需要在数字上添加0x30以使其打印该数字的字符。
func TestMain(m *testing.M) {
mysqld, err := mysqltest.NewMysqld(nil)
if err != nil {
log.Fatal("runTests: failed", err)
}
defer mysqld.Stop()
dbm, err = gorm.Open("mysqld", mysqld.Datasource("test", "", "", 0 ))
if err != nil {
log.Fatal("db connection error:", err)
}
defer dbm.Close()
code := m.Run()
os.Exit(code)
}
boot_sect.asm
; 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
print_string.asm
这个程序的输出是我所期望的,但是当我试图在数字上添加0x30以获得数字的ASCII码时,输出是乱码。是否有一些技巧或我错过了一些关键点?
谢谢!
答案 0 :(得分:1)
原始问题的答案:
因为你add byte [bx], cl
将数字写入缓冲区,并且缓冲区已经包含'0'
,所以它第一次正常工作。第二次调用print_hex
会再次产生乱码,因为HEX_OUT
内容已被修改(琐事:首先打印的十六进制数字也允许正确打印某些第二个值?)。
现在只是为了好玩,我添加我可能会为自己做print_hex
。也许它会为你的x86 ASM编程提供额外的想法,我试着对它做出很多评论来解释为什么我会按照我这样做的方式做事:
首先,我将分离格式化函数,因此我最终可以在其他地方重用它,因此输入是数字和目标缓冲区指针。我使用LUT(查找表)进行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 '0123456789ABCDEF'
然后为方便起见,也可以添加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),所以一些bug可能已经滑入。我没有16b环境来验证它是完整的启动代码。)