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

标签 assembly bootloader gnu-assembler 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  

最佳答案

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

KEYBOARD - GET KEYSTROKE

AH = 00h

Return:
AH = BIOS scan code
AL = ASCII character

你的想法是正确的:

mov 0, %ah
int $0x16  #BIOS Keyboard Service 
ret 

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

mov $0, %ah
int $0x16  #BIOS Keyboard Service 
ret 

或者,您可以使用以下方法将 AH 归零:

xor %ah, %ah
<小时/>

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

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

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

<小时/>

最后,您手动编码长跳转以进行重新启动:

.byte 0x0ea 
.word 0x0000 
.word 0xffff 

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

jmp $0xffff,$0x0000

关于assembly - INT 16h/AH=0h 不等待引导加载程序中的按键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37101521/

相关文章:

Android - 在 x86 汇编程序中调用 C 函数而不重新分配文本

assembly - 宏替换GAS中的常数

assembly - AVR 微架构如何能够仅在 1 个时钟周期内从 GP 寄存器获取 2 个操作数到 ALU?

assembly - 如何在gdb中调用程序集?

arduino - STK500v2 引导加载程序仅在上电复位时加载,而不是在引脚/WDT 复位时加载

windows - Windows XP 引导加载程序 (NTLDR) 是否注意内核导入声明?

linux-kernel - ARM 机器/主板 ID

assembly - 在下面的代码中,arm 汇编代码 .long 的作用是什么?

assembly - 如何使用 SSE 计算模数/余数?

assembly - GAS 汇编器操作数类型不匹配 `cmovz'