2

一直在尝试测试使用 FASM 或 NASM 编译的简单 ISA Option Rom 程序,该程序仅打印出简单的“Hello World”消息。

问题是在 QEMU 中测试时,我得到了几个意外字符,而不是打印字符串。但是,这些属性可以工作并更改文本颜色,但经过数小时的 Google 搜索后无法弄清楚。

最好的猜测是ORG需要设置命令,因为ALSIusing复制了错误的内存地址LODSB。有任何想法吗??

use16        ; ISA module operates in the 16-bit segment.

DB      55h, 0AAh          ; Boot signature
DB      01h               ; Block size in sectors (200h each)



xor ax, ax ; make it zero
mov ds, ax

start:

mov si, text_string     ; Put string position into SI
call print_string       ; Call our string-printing routine

.bounce:
jmp .bounce                   ; Jump here - infinite loop!

print_string:                   ; Routine: output string in SI to screen

.repeat:
   ;mov ah, 09h             ; int 10h 'print char' function
   ; mov bh, 0x00
   ; mov bl, 0x03
   ; mov cx, 1h

lodsb                   ; Get character from string

or al,al
jz .done

mov ah, 0x0E
int 0x10

; If char is zero, end of string
; int 10h                 ; Otherwise, print it

mov bh, 00h
mov ah, 03h
int 10h
mov ah, 02h
mov bh, 00h
inc dl
int 10h
jmp .repeat

.done:
mov al, 'D'
mov bh,0x00
mov cx,1
mov ah,0ah
int 10h

ret

text_string db 'Hello World!',,0
times 512-($-$$) db 0   
4

2 回答 2

4

主要问题是您将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,并且我找不到有关调用约定的任何特定文档,因此我不确定您是否需要保留更改的所有寄存器。我使用reeLinux 下的程序查看了自己 PC 中的选项 ROM 。我注意到的是他们使用pushapopa保存和恢复所有通用寄存器,并且他们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 lodsbcs 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
于 2018-03-01T14:56:38.457 回答
0

解决了打印到屏幕的问题。必须在代码前面加上:

  pusha
  cli
  mov   ax,cs
  mov   ds,ax
  mov   es,ax

这是任何人都可以学习的最终代码:

use16        ; ISA module operates in the 16-bit segment.

ROM_SIZE_IN_BLOCK = 1  ; 1 means ROM size is 1 block (512 bytes)
ROM_SIZE_IN_BYTE = ROM_SIZE_IN_BLOCK * 512

DB      55h, 0AAh          ; Boot signature
DB      01h               ; Block size in sectors (200h each)





start:


      pusha
      cli
      mov   ax,cs
      mov   ds,ax
      mov   es,ax
      call cls


    mov si, text_string     ; Put string position into SI
    call print_string       ; Call our string-printing routine
    ;retf

   .bounce:
    jmp .bounce                   ; Jump here - infinite loop!





print_string:                   ; Routine: output string in SI to screen

.repeat:
    mov ah, 09h             ; int 10h 'print char' function
    mov bh, 0x00
    mov bl, 0x03
    mov cx, 01h
    cld
    lodsb                   ; Get character from string
    cmp al, 0
    je .done


    ; If char is zero, end of string
    int 10h                 ; Otherwise, print it
    mov bh, 00h
    mov ah, 03h
    int 10h
    mov ah, 02h
    mov bh, 00h
    inc dl
    int 10h
    jmp .repeat

 .done:

ret

clrscr:
   mov dh, 0
   mov dl, 0
   call set_cursor
   mov ah, 0x0a
   mov al, ' '
   mov bh, 0
   mov cx, 2000
   int 0x10
   ret


cls:
  pusha
  mov ah, 0x00
  mov al, 0x03  ; text mode 80x25 16 colours
  int 0x10
  popa
  ret

set_cursor:
   mov ah, 0x02
   int 0x10
   ret


text_string db "Hello World!",0

 ;times 512-($-$$) db 0

times (ROM_SIZE_IN_BYTE-$) db 0 ; use 00h as the padding bytes until we reach the ROM size

 ;patch_byte is calculated and automagically inserted below
PREV_CHKSUM = 0
repeat $
   load CHKSUM byte from %-1
   CHKSUM = (PREV_CHKSUM + CHKSUM) mod 0x100
   PREV_CHKSUM = CHKSUM
end repeat
store byte (0x100 - CHKSUM) at ($-1)  ; store the patch_byte         

仍然无法让它与 BOCHS 一起使用,但 Qemu 现在喜欢它!

于 2018-03-02T00:45:06.973 回答