INT 16h/AH=0h 不等待引导加载程序中的按键

Mag*_*man 5 assembly gnu-assembler bootloader att x86-16

我使用 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 $0x0e, %ah
      int  $0x10
      jmp  .writeStringIn
 .writeStringOut:
 ret
#Gets the pressed key 
.GetPressedKey:
 mov 0, %ah
 int $0x16  #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  
Run Code Online (Sandbox Code Playgroud)

Mic*_*tch 3

Int 0x16 AH=0将等待击键:

键盘 - 获取击键

AH = 00h

Return:
AH = BIOS scan code
AL = ASCII character
Run Code Online (Sandbox Code Playgroud)

你的想法是正确的:

mov 0, %ah
int $0x16  #BIOS Keyboard Service 
ret 
Run Code Online (Sandbox Code Playgroud)

问题在于mov 0, %ah将 0 视为内存操作数。在这种情况下,这与将 DS:[0] 处的字节值移动到AH相同。您只想将立即(常量)值 0 移动到AH。在 AT&T 语法中,立即值前面带有美元符号$。如果一个值没有前缀,$GNU 汇编器会假定它是一个内存引用。代码应该是:

mov $0, %ah
int $0x16  #BIOS Keyboard Service 
ret 
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用以下命令将AH归零:

xor %ah, %ah
Run Code Online (Sandbox Code Playgroud)

当引导加载程序加载时,您的代码会对DS寄存器的值做出一些假设,并且它不会设置自己的堆栈。有关编写可用于更广泛的仿真器和真实硬件的引导加载程序的一些良好实践,您可能希望查看我在之前的 Stackoverflow 答案中给出的一些常规引导加载程序技巧。

此外,在您的情况下,我可能会在代码之后移动您的数据,并在开始时删除 JMP ,因为您没有使用BIOS 参数块。一些 BIOS 会在引导扇区的开头查找JMP,并假设您有 BPB,并且可能实际上会覆盖您的代码,认为它正在填充数据结构的值。避免这种情况的最佳方法是,如果没有 BPB,则避免将JMP作为第一条指令。

您可能在表现出所需行为的模拟器下运行,但如果您将此引导加载程序移动到其他环境(甚至其他模拟器),您可能会发现它无法按预期工作。


最后,您手动编码一个长跳转来重新启动:

.byte 0x0ea 
.word 0x0000 
.word 0xffff 
Run Code Online (Sandbox Code Playgroud)

尽管您可能必须在某些旧版本的MASMTASM中执行此操作,但具有 AT&T 语法的 GNU 汇编器支持这样做:

jmp $0xffff,$0x0000
Run Code Online (Sandbox Code Playgroud)