我使用带有AT& T语法的GNU Assembler编写了我的第一个bootloader。假设将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
答案 0 :(得分:3)
Int 0x16 AH=0会等待击键:
键盘 - 获得钥匙扣
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 寄存器的值做出一些假设,并且它不会设置自己的堆栈。对于编写可以处理各种仿真器和真实硬件的引导加载程序的一些好的做法,您可能希望查看我之前的Stackoverflow答案中给出的一些General Bootloader Tips。
此外,在您的情况下,我可能会在代码之后移动您的数据,并在开头删除 JMP ,因为您没有使用BIOS Parameter Block。一些BIOS会在bootsector的开头寻找 JMP ,并假设你有一个BPB,并且实际上可能会覆盖你的代码,认为它填充了数据结构的值。避免这种情况的最好方法是简单地避免使用 JMP 作为第一条指令 IF 您没有BPB。
您可能在展示所需行为的模拟器下运行,但如果您要将此引导加载程序移动到其他环境(甚至其他模拟器),您可能会发现它无法正常工作。
最后,您手动编码一个长跳转来进行重启:
.byte 0x0ea
.word 0x0000
.word 0xffff
虽然您可能不得不在某些旧版本的 MASM 或 TASM 中执行此操作,但具有AT& T语法的GNU汇编程序支持这样做:
jmp $0xffff,$0x0000