INT 16h/AH=0h 不等待我的引导加载程序中的击键
INT 16h/AH=0h doesn't wait for keystroke in my bootloader
我使用 GNU 汇编程序和 AT&T 语法编写了我的第一个引导加载程序。假设在屏幕上打印 hello world
然后通知用户按任意键将导致重新启动。只有按下某个键后才会启动重启。我的引导加载程序代码不等待按键并在打印信息后自动重启。为什么此代码不等待击键,我该如何解决?
我的引导扇区代码:
#generate 16-bit code
.code16
#hint the assembler that here is the executable code located
.text
.globl _start;
#boot code entry
_start:
jmp _boot #jump to boot code
welcome: .asciz "Hello, World\n\r" #here we define the string
AnyKey: .asciz "Press any key to reboot...\n\r"
.macro mWriteString str #macro which calls a function to print a string
leaw \str, %si
call .writeStringIn
.endm
#function to print the string
.writeStringIn:
lodsb
orb %al, %al
jz .writeStringOut
movb [=10=]x0e, %ah
int [=10=]x10
jmp .writeStringIn
.writeStringOut:
ret
#Gets the pressed key
.GetPressedKey:
mov 0, %ah
int [=10=]x16 #BIOS Keyboard Service
ret
.Reboot:
mWriteString AnyKey
call .GetPressedKey
#Sends us to the end of the memory
#causing reboot
.byte 0x0ea
.word 0x0000
.word 0xffff
_boot:
mWriteString welcome
call .Reboot
#move to 510th byte from the start and append boot signature
. = _start + 510
.byte 0x55
.byte 0xaa
Int 0x16 AH=0 将等待击键:
KEYBOARD - GET KEYSTROKE
AH = 00h
Return:
AH = BIOS scan code
AL = ASCII character
你的想法是对的:
mov 0, %ah
int [=11=]x16 #BIOS Keyboard Service
ret
问题是 mov 0, %ah
将 0 视为内存操作数。在这种情况下,它与将 DS:[0] 处的字节值移动到 AH 相同。您只想将立即(常量)值 0 移动到 AH。在 AT&T 语法中,立即值前面加上美元符号 $
。如果一个值没有前缀 $
GNU 汇编器假定它是一个内存引用。代码应该是:
mov [=12=], %ah
int [=12=]x16 #BIOS Keyboard Service
ret
或者您可以使用以下方法将 AH 归零:
xor %ah, %ah
您的代码在您的引导加载程序加载时对 DS 寄存器的值做了一些假设,并且它没有设置自己的堆栈。有关编写适用于更广泛的仿真器和真实硬件的引导加载程序的一些良好实践,您可能希望查看我在之前的 Whosebug 回答中给出的一些 。
此外,在您的情况下,我可能会在代码之后移动您的数据并删除开头的 JMP ,因为您没有使用 BIOS Parameter Block 。某些 BIOS 会在引导扇区的开头寻找 JMP,并假设您有一个 BPB,并且可能实际上会覆盖您的代码,认为它正在填充数据结构的值。避免这种情况的最好方法是简单地避免将 JMP 作为第一条指令 IF 你没有 BPB。
您可能 运行 在表现出您需要的行为的模拟器下,但如果您将此引导加载程序移动到其他环境(甚至其他模拟器),您可能会发现它不起作用预期。
最后手动编码跳远重启:
.byte 0x0ea
.word 0x0000
.word 0xffff
虽然在某些旧版本的 MASM 或 TASM 中您可能不得不这样做,但具有 AT&T 语法的 GNU 汇编程序支持这样做这样:
jmp [=15=]xffff,[=15=]x0000
我使用 GNU 汇编程序和 AT&T 语法编写了我的第一个引导加载程序。假设在屏幕上打印 hello world
然后通知用户按任意键将导致重新启动。只有按下某个键后才会启动重启。我的引导加载程序代码不等待按键并在打印信息后自动重启。为什么此代码不等待击键,我该如何解决?
我的引导扇区代码:
#generate 16-bit code
.code16
#hint the assembler that here is the executable code located
.text
.globl _start;
#boot code entry
_start:
jmp _boot #jump to boot code
welcome: .asciz "Hello, World\n\r" #here we define the string
AnyKey: .asciz "Press any key to reboot...\n\r"
.macro mWriteString str #macro which calls a function to print a string
leaw \str, %si
call .writeStringIn
.endm
#function to print the string
.writeStringIn:
lodsb
orb %al, %al
jz .writeStringOut
movb [=10=]x0e, %ah
int [=10=]x10
jmp .writeStringIn
.writeStringOut:
ret
#Gets the pressed key
.GetPressedKey:
mov 0, %ah
int [=10=]x16 #BIOS Keyboard Service
ret
.Reboot:
mWriteString AnyKey
call .GetPressedKey
#Sends us to the end of the memory
#causing reboot
.byte 0x0ea
.word 0x0000
.word 0xffff
_boot:
mWriteString welcome
call .Reboot
#move to 510th byte from the start and append boot signature
. = _start + 510
.byte 0x55
.byte 0xaa
Int 0x16 AH=0 将等待击键:
KEYBOARD - GET KEYSTROKE
AH = 00h Return: AH = BIOS scan code AL = ASCII character
你的想法是对的:
mov 0, %ah
int [=11=]x16 #BIOS Keyboard Service
ret
问题是 mov 0, %ah
将 0 视为内存操作数。在这种情况下,它与将 DS:[0] 处的字节值移动到 AH 相同。您只想将立即(常量)值 0 移动到 AH。在 AT&T 语法中,立即值前面加上美元符号 $
。如果一个值没有前缀 $
GNU 汇编器假定它是一个内存引用。代码应该是:
mov [=12=], %ah
int [=12=]x16 #BIOS Keyboard Service
ret
或者您可以使用以下方法将 AH 归零:
xor %ah, %ah
您的代码在您的引导加载程序加载时对 DS 寄存器的值做了一些假设,并且它没有设置自己的堆栈。有关编写适用于更广泛的仿真器和真实硬件的引导加载程序的一些良好实践,您可能希望查看我在之前的 Whosebug 回答中给出的一些
此外,在您的情况下,我可能会在代码之后移动您的数据并删除开头的 JMP ,因为您没有使用 BIOS Parameter Block 。某些 BIOS 会在引导扇区的开头寻找 JMP,并假设您有一个 BPB,并且可能实际上会覆盖您的代码,认为它正在填充数据结构的值。避免这种情况的最好方法是简单地避免将 JMP 作为第一条指令 IF 你没有 BPB。
您可能 运行 在表现出您需要的行为的模拟器下,但如果您将此引导加载程序移动到其他环境(甚至其他模拟器),您可能会发现它不起作用预期。
最后手动编码跳远重启:
.byte 0x0ea
.word 0x0000
.word 0xffff
虽然在某些旧版本的 MASM 或 TASM 中您可能不得不这样做,但具有 AT&T 语法的 GNU 汇编程序支持这样做这样:
jmp [=15=]xffff,[=15=]x0000