如何使用 INT 10H 显示寄存器值
How to display register value using INT 10H
我知道我可以打印一个 ASCII 字符,如果它在 AL 中表示为:
DrawChar:
MOV AL, 0x45
MOV AH, 0x0E
MOV BL, 0x07
MOV BH, 0x00
INT 0x10
RET
有什么方法可以使用 INT 10H 来打印实际的寄存器值吗?例如 AL 是 0x45上面,所以它会打印 45(不必是十六进制代表)。我在 16 位实模式引导加载程序中执行此操作。
我给你一个打印16位寄存器的方法。这通过将 16 位寄存器向左旋转 4 位到最低有效位然后隔离这 4 位来一次打印一个半字节(4 位)。然后我们打印该值的十六进制数字,即 0
到 9
和 A
到 F
。我们对 16 位字中的 4 个半字节中的每一个执行 4 次。
示例中提供了两个 16 位十六进制打印函数。 选择一个适合您的环境:
print_hex_word
适用于任何 8086/8088 处理器或更高版本。
print_hex_word
这是一个较小的版本,可以在 16 位实模式下的任何 80186/80188 处理器上运行。
两种变体都要求您:
- 将包含页码和前景颜色的 2 字节值压入堆栈(图形模式)。有关 Int 10h/AH=0eh
的更多信息,请参阅 Ralf Brown 的中断列表
- 将 2 字节的值压入要打印的堆栈。
下面的示例代码包含一个最小引导加载程序作为示例:
bits 16
org 0x7c00
section .text
global _start
_start:
; Set segment registers to 0
xor ax, ax
mov ds, ax
mov es, ax
; Set stack pointer just below bootloader
mov ss, ax
mov sp, 0x7c00
; Clear direction flag (forward movement)
cld
; print_hex_word/print_hex_word_186 take a second parameter
; that is the page number (upper 8 bits) and foreground color
; in lower 8 bits. We just want 0x0000 so push it on the
; stack first
push ax
; This test just prints SS and SP to the display
push ss ; Push on stack as 1st parameter
; In this case display value in SS
call print_hex_word ; Print 16-bit value as hex
add sp, 2 ; Cleanup stack after call
push sp ; Push on stack as 1st parameter
; In this case display value in SP
call print_hex_word ; Print 16-bit value as hex
add sp, 2 ; Cleanup stack after call
; Print value 0xaa55 to the display
mov ax, 0xaa55
push ax ; Push on stack as 1st parameter
; In this case display value in 0xAA55
call print_hex_word ; Print 16-bit value as hex
cli ; Disable interrupts
hlt ; Halt processor
; Print 16 bit value passed on stack as first parameter
; in hexadecimal. Use page number and foreground color
; passed in second parameter. This routine will work on 8086+
; processors. This code takes advantage of packed BCD to
; determine the ASCII values to print. This code could have
; used compare and branch to do the same or a translation table.
print_hex_word:
push bp
mov bp, sp ; BP=SP, on 8086 can't use sp in memory operand
push dx ; Save all registers we clobber
push cx
push bx
push ax
mov cx, 0x0404 ; CH = number of nibbles to process = 4 (4*4=16 bits)
; CL = Number of bits to rotate each iteration = 4 (a nibble)
mov dx, [bp+4] ; DX = word parameter on stack at [bp+4] to print
mov bx, [bp+6] ; BX = page / foreground attr is at [bp+6]
.loop:
rol dx, cl ; Roll 4 bits left. Lower nibble is value to print
mov ax, 0x0e0f ; AH=0E (BIOS tty print),AL=mask to get lower nibble
and al, dl ; AL=copy of lower nibble
add al, 0x90 ; Work as if we are packed BCD
daa ; Decimal adjust after add.
; If nibble in AL was between 0 and 9, then CF=0 and
; AL=0x90 to 0x99
; If nibble in AL was between A and F, then CF=1 and
; AL=0x00 to 0x05
adc al, 0x40 ; AL=0xD0 to 0xD9
; or AL=0x41 to 0x46
daa ; AL=0x30 to 0x39 (ASCII '0' to '9')
; or AL=0x41 to 0x46 (ASCII 'A' to 'F')
int 0x10 ; Print ASCII character in AL
dec ch
jnz .loop ; Go back if more nibbles to process
pop ax ; Restore registers
pop bx
pop cx
pop dx
pop bp
ret
TIMES 510-($-$$) db 0
DW 0xaa55
使用PUSHA and POPA保存和恢复寄存器AX、CX的print_hex_word
80186+版本, DX, BX, 原版SP, BP, SI 和 DI:
; Print 16 bit value passed on stack as first parameter
; in hexadecimal. This routine will work on 80186+ processors
; Use page number and foreground color passed in second parameter
print_hex_word:
pusha ; Save all registers, 16 bytes total
mov bp, sp ; BP=SP, on 8086 can't use sp in memory operand
mov cx, 0x0404 ; CH = number of nibbles to process = 4 (4*4=16 bits)
; CL = Number of bits to rotate each iteration = 4 (a nibble)
mov dx, [bp+18] ; DX = word parameter on stack at [bp+18] to print
mov bx, [bp+20] ; BX = page / foreground attr is at [bp+20]
.loop:
rol dx, cl ; Roll 4 bits left. Lower nibble is value to print
mov ax, 0x0e0f ; AH=0E (BIOS tty print),AL=mask to get lower nibble
and al, dl ; AL=copy of lower nibble
add al, 0x90 ; Work as if we are packed BCD
daa ; Decimal adjust after add.
; If nibble in AL was between 0 and 9, then CF=0 and
; AL=0x90 to 0x99
; If nibble in AL was between A and F, then CF=1 and
; AL=0x00 to 0x05
adc al, 0x40 ; AL=0xD0 to 0xD9
; or AL=0x41 to 0x46
daa ; AL=0x30 to 0x39 (ASCII '0' to '9')
; or AL=0x41 to 0x46 (ASCII 'A' to 'F')
int 0x10 ; Print ASCII character in AL
dec ch
jnz .loop ; Go back if more nibbles to process
popa ; Restore all the registers
ret
该代码使用一些打包的二进制编码十进制 (BCD) 技巧将 4 位值转换为十六进制数字。有关压缩 BCD 算法的更多信息,请参见 处理压缩 BCD 数 部分的 tutorial。
要 assemble 您可以使用此引导加载程序:
nasm -f bin boot.asm -o boot.bin
它可以在 Linux 命令行中使用 QEMU 进行测试,如下所示:
qemu-system-i386 -fda boot.bin
调试建议
如果您使用 BOCHS,您可以使用其内置调试器单步执行引导加载程序,并在代码执行时显示寄存器和内存的内容。
我知道我可以打印一个 ASCII 字符,如果它在 AL 中表示为:
DrawChar:
MOV AL, 0x45
MOV AH, 0x0E
MOV BL, 0x07
MOV BH, 0x00
INT 0x10
RET
有什么方法可以使用 INT 10H 来打印实际的寄存器值吗?例如 AL 是 0x45上面,所以它会打印 45(不必是十六进制代表)。我在 16 位实模式引导加载程序中执行此操作。
我给你一个打印16位寄存器的方法。这通过将 16 位寄存器向左旋转 4 位到最低有效位然后隔离这 4 位来一次打印一个半字节(4 位)。然后我们打印该值的十六进制数字,即 0
到 9
和 A
到 F
。我们对 16 位字中的 4 个半字节中的每一个执行 4 次。
示例中提供了两个 16 位十六进制打印函数。 选择一个适合您的环境:
print_hex_word
适用于任何 8086/8088 处理器或更高版本。print_hex_word
这是一个较小的版本,可以在 16 位实模式下的任何 80186/80188 处理器上运行。
两种变体都要求您:
- 将包含页码和前景颜色的 2 字节值压入堆栈(图形模式)。有关 Int 10h/AH=0eh 的更多信息,请参阅 Ralf Brown 的中断列表
- 将 2 字节的值压入要打印的堆栈。
下面的示例代码包含一个最小引导加载程序作为示例:
bits 16
org 0x7c00
section .text
global _start
_start:
; Set segment registers to 0
xor ax, ax
mov ds, ax
mov es, ax
; Set stack pointer just below bootloader
mov ss, ax
mov sp, 0x7c00
; Clear direction flag (forward movement)
cld
; print_hex_word/print_hex_word_186 take a second parameter
; that is the page number (upper 8 bits) and foreground color
; in lower 8 bits. We just want 0x0000 so push it on the
; stack first
push ax
; This test just prints SS and SP to the display
push ss ; Push on stack as 1st parameter
; In this case display value in SS
call print_hex_word ; Print 16-bit value as hex
add sp, 2 ; Cleanup stack after call
push sp ; Push on stack as 1st parameter
; In this case display value in SP
call print_hex_word ; Print 16-bit value as hex
add sp, 2 ; Cleanup stack after call
; Print value 0xaa55 to the display
mov ax, 0xaa55
push ax ; Push on stack as 1st parameter
; In this case display value in 0xAA55
call print_hex_word ; Print 16-bit value as hex
cli ; Disable interrupts
hlt ; Halt processor
; Print 16 bit value passed on stack as first parameter
; in hexadecimal. Use page number and foreground color
; passed in second parameter. This routine will work on 8086+
; processors. This code takes advantage of packed BCD to
; determine the ASCII values to print. This code could have
; used compare and branch to do the same or a translation table.
print_hex_word:
push bp
mov bp, sp ; BP=SP, on 8086 can't use sp in memory operand
push dx ; Save all registers we clobber
push cx
push bx
push ax
mov cx, 0x0404 ; CH = number of nibbles to process = 4 (4*4=16 bits)
; CL = Number of bits to rotate each iteration = 4 (a nibble)
mov dx, [bp+4] ; DX = word parameter on stack at [bp+4] to print
mov bx, [bp+6] ; BX = page / foreground attr is at [bp+6]
.loop:
rol dx, cl ; Roll 4 bits left. Lower nibble is value to print
mov ax, 0x0e0f ; AH=0E (BIOS tty print),AL=mask to get lower nibble
and al, dl ; AL=copy of lower nibble
add al, 0x90 ; Work as if we are packed BCD
daa ; Decimal adjust after add.
; If nibble in AL was between 0 and 9, then CF=0 and
; AL=0x90 to 0x99
; If nibble in AL was between A and F, then CF=1 and
; AL=0x00 to 0x05
adc al, 0x40 ; AL=0xD0 to 0xD9
; or AL=0x41 to 0x46
daa ; AL=0x30 to 0x39 (ASCII '0' to '9')
; or AL=0x41 to 0x46 (ASCII 'A' to 'F')
int 0x10 ; Print ASCII character in AL
dec ch
jnz .loop ; Go back if more nibbles to process
pop ax ; Restore registers
pop bx
pop cx
pop dx
pop bp
ret
TIMES 510-($-$$) db 0
DW 0xaa55
使用PUSHA and POPA保存和恢复寄存器AX、CX的print_hex_word
80186+版本, DX, BX, 原版SP, BP, SI 和 DI:
; Print 16 bit value passed on stack as first parameter
; in hexadecimal. This routine will work on 80186+ processors
; Use page number and foreground color passed in second parameter
print_hex_word:
pusha ; Save all registers, 16 bytes total
mov bp, sp ; BP=SP, on 8086 can't use sp in memory operand
mov cx, 0x0404 ; CH = number of nibbles to process = 4 (4*4=16 bits)
; CL = Number of bits to rotate each iteration = 4 (a nibble)
mov dx, [bp+18] ; DX = word parameter on stack at [bp+18] to print
mov bx, [bp+20] ; BX = page / foreground attr is at [bp+20]
.loop:
rol dx, cl ; Roll 4 bits left. Lower nibble is value to print
mov ax, 0x0e0f ; AH=0E (BIOS tty print),AL=mask to get lower nibble
and al, dl ; AL=copy of lower nibble
add al, 0x90 ; Work as if we are packed BCD
daa ; Decimal adjust after add.
; If nibble in AL was between 0 and 9, then CF=0 and
; AL=0x90 to 0x99
; If nibble in AL was between A and F, then CF=1 and
; AL=0x00 to 0x05
adc al, 0x40 ; AL=0xD0 to 0xD9
; or AL=0x41 to 0x46
daa ; AL=0x30 to 0x39 (ASCII '0' to '9')
; or AL=0x41 to 0x46 (ASCII 'A' to 'F')
int 0x10 ; Print ASCII character in AL
dec ch
jnz .loop ; Go back if more nibbles to process
popa ; Restore all the registers
ret
该代码使用一些打包的二进制编码十进制 (BCD) 技巧将 4 位值转换为十六进制数字。有关压缩 BCD 算法的更多信息,请参见 处理压缩 BCD 数 部分的 tutorial。
要 assemble 您可以使用此引导加载程序:
nasm -f bin boot.asm -o boot.bin
它可以在 Linux 命令行中使用 QEMU 进行测试,如下所示:
qemu-system-i386 -fda boot.bin
调试建议
如果您使用 BOCHS,您可以使用其内置调试器单步执行引导加载程序,并在代码执行时显示寄存器和内存的内容。