主要问题是您将DS初始化为 0。当 BIOS 将控制权传递给您的 init 例程时,它将对选项 ROM 位置 +3 执行 FAR CALL。FAR 调用会将CS设置为加载选项 ROM 的段,并将 IP(指令指针)设置为 3。3 是刚刚超过签名和大小字节的偏移量。
通过将DS设置为零,您将访问相对于 0x0000 段的字符串。您想使用加载选项 ROM 的段。为此,您将DS初始化为CS寄存器的值。代替:
xor ax, ax ; make it zero
mov ds, ax
你来做这件事:
mov ax, cs ; CS contains segment we are running in
mov ds, ax ; so copy it to DS
您还应该为使用CLD转发的字符串指令设置方向标志。在调用我们的选项 ROM 代码之前,您不能保证 BIOS 设置它。
由于我从未编写过选项 ROM,并且我找不到有关调用约定的任何特定文档,因此我不确定您是否需要保留更改的所有寄存器。我使用ree
Linux 下的程序查看了自己 PC 中的选项 ROM 。我注意到的是他们使用pusha
并popa
保存和恢复所有通用寄存器,并且他们push
/pop
来保存/恢复单个段寄存器。在您自己的选项 ROM 中执行此操作可能是一种好习惯。可以追溯到旧的 Phoenix BIOS的一个要求是在大小字节之后需要有一个 NEAR JMP 到初始化代码的入口点。
完成选项 ROM 初始化例程后,您使用retf
(FAR return) 返回 BIOS,以便 BIOS 可以继续扫描其他选项 ROM 并完成启动序列。
由于打印例程中存在一些故障,因此我对您的代码进行了一些修复。此代码应该可以工作:
use16 ; ISA module operates in the 16-bit segment.
DB 55h, 0AAh ; Boot signature
DB 01h ; Block size in sectors (200h each)
jmp start ; NearJMP part of Phoenix BIOS specification
start:
pushf ; Save the flags as we modify direction bit
pusha ; Save all general purpose registers
push ds ; we modify DS so save it
cld ; Ensure forward string direction
mov ax, cs ; CS contains segment we are running in
mov ds, ax ; so copy it to DS
mov si, text_string ; Put string position into SI
call print_string ; Call our string-printing routine
pop ds ; Restore all registers and flags we saved
popa
popf
retf ; Far return to exit option init routine
print_string: ; Routine: output string in SI to screen
mov ah, 0eh ; BIOS tty Print
xor bx, bx ; Set display page to 0 (BL)
jmp .getch
.repeat:
int 10h ; print character
.getch:
lodsb ; Get character from string
test al,al ; Have we reached end of string?
jnz .repeat ; if not process next character
.end:
ret
; String ends with 0dh (Carriage return) and 0ah (linefeed) to
; advance cursor to the beginning of next line
text_string db 'Hello World!', 0dh, 0ah, 0
times 512-($-$$) db 0
注意:代码使用pusha
/popa
指令仅在 80186+ 处理器上可用。如果您的目标是 8086/8088,那么您需要单独推送和弹出您修改的每个寄存器。
也可以不使用DS寄存器段并使用CS覆盖覆盖LODSB。可以修改为. 通过这样做,您不需要保存和恢复DS,因为DS将保持不变。您也不需要将CS复制到DS。如果您替换为以下内容,您同样可以放弃保存和恢复标志并使用CLD设置方向位:cs lodsb
cs lodsb
mov al, [cs:si]
inc si
简化的代码可能如下所示:
use16 ; ISA module operates in the 16-bit segment.
DB 55h, 0AAh ; Boot signature
DB 01h ; Block size in sectors (200h each)
jmp start ; NearJMP part of Phoenix BIOS specification
start:
pusha ; Save all generel purpose registers
mov si, text_string ; Put string position into SI
call print_string ; Call our string-printing routine
popa ; Restore all general purpose registers
retf ; Far return to exit option init routine
print_string: ; Routine: output string in SI to screen
mov ah, 0eh ; BIOS tty Print
xor bx, bx ; Set display page to 0 (BL)
jmp .getch
.repeat:
int 10h ; print character
.getch:
mov al, [cs:si]
inc si
test al,al ; Have we reached end of string?
jnz .repeat ; if not process next character
.end:
ret
; String ends with 0x0d (Carriage return) and 0x0a (linefeed) to
; advance cursor to the beginning of next line
text_string db 'Hello World!', 0dh, 0ah, 0
times 512-($-$$) db 0
我用来在 Debian Linux 上测试的 Bochs 配置文件是:
# configuration file generated by Bochs
plugin_ctrl: unmapped=1, biosdev=1, speaker=1, extfpuirq=1, parallel=1, serial=1, iodebug=1
config_interface: textconfig
display_library: x
memory: host=32, guest=32
romimage: file="/usr/local/share/bochs/BIOS-bochs-latest", address=0x0, options=none
vgaromimage: file="/usr/local/share/bochs/VGABIOS-lgpl-latest"
# no floppyb
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=none
ata0-slave: type=none
ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata1-master: type=none
ata1-slave: type=none
ata2: enabled=0
ata3: enabled=0
optromimage1: file="optrom.bin", address=0xd0000
pci: enabled=1, chipset=i440fx
vga: extension=vbe, update_freq=5, realtime=1
cpu: count=1:1:1, ips=4000000, quantum=16, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
cpuid: level=6, stepping=3, model=3, family=6, vendor_string="GenuineIntel", brand_string=" Intel(R) Pentium(R) 4 CPU "
cpuid: mmx=1, apic=xapic, simd=sse2, sse4a=0, misaligned_sse=0, sep=1, movbe=0, adx=0
cpuid: aes=0, sha=0, xsave=0, xsaveopt=0, x86_64=1, 1g_pages=0, pcid=0, fsgsbase=0
cpuid: smep=0, smap=0, mwait=1
print_timestamps: enabled=0
debugger_log: -
magic_break: enabled=0
port_e9_hack: enabled=0
private_colormap: enabled=0
clock: sync=none, time0=local, rtc_sync=0
# no cmosimage
# no loader
log: -
logprefix: %t%e%d
debug: action=ignore
info: action=report
error: action=report
panic: action=ask
keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none
mouse: type=ps2, enabled=0, toggle=ctrl+mbutton
speaker: enabled=1, mode=system
parport1: enabled=1, file=none
parport2: enabled=0
com1: enabled=1, mode=null
com2: enabled=0
com3: enabled=0
com4: enabled=0
此配置假定可选 ROM 位于文件中optrom.bin
,并将加载到内存地址 0xd0000
选项 ROM 必须计算校验和并将其放置在映像文件的最后一个字节中。QEMU 提供了一个可用于此目的的脚本。要更新图像的校验和,您可以执行以下操作:
python signrom.py inputimagefile outputimagefile