Int 10, ah 0eh 在引导加载程序中给出了错误的输出
Int 10, ah 0eh gives wrong output in a bootloader
我正在尝试编写自己的 Os 并且我需要一些 print_string 函数来在 sI 中打印字符串 它是这样的
print_string:
Pusha
mov ah,0Eh
.repeat:
lodsb
cmp al,0
je .done
int 10h
Jmp .repeat
.done:
Popa
Ret
但是
中的输出
mov si,Msg
call print_string
就像Wn=Wn=
而消息是 "hello"
这不是一个最小的完整示例,但这个问题很常见。即使没有看到其余代码,我猜您在启动时处理器(或模拟处理器)使用的段与汇编代码使用的偏移量不匹配。
对于 16 位 segment:offset addressing,有许多指向相同物理内存地址的段和偏移量的组合。 BIOS 最常用的是 0x07c0:0x0000 和 0x0000:0x0000。 (0x07c0 << 4)+0x0000 = 物理地址 0x07c00。 (0x0000 << 4) + 0x7c00 = 物理地址也为 0x07c00。这个想法是您希望您的 ORG 指令反映您要使用的偏移部分,然后在引导加载程序启动时手动设置 DS 。 NASM 中缺少 ORG 指令(使用 -f bin
构建时)默认为 org 0x0000
.
我有这样的一般引导加载程序提示:
- When the BIOS jumps to your code you can't rely on CS,DS,ES,SS,SP registers having valid or expected values. They should be set up appropriately when your bootloader starts. You can only be guaranteed that your bootloader will be loaded and run from physical address 0x00007c00 and that the boot drive number is loaded into the DL register.
[snip]
- The direction flag used by
lodsb
, movsb
etc could be either set or cleared. If the direction flag is set improperly SI/DI registers may be adjusted in the wrong direction. Use STD
/CLD
to set it to the direction you wish (CLD=forward/STD=backwards). In this case the code assumes forward movement so one should use CLD
. More on this can be found in an instruction set reference
您不能依赖方向 DF 标志(LODSB 需要)和段寄存器(如 DS)作为特定值(或正确值),当您引导加载程序启动。您需要在代码的开头明确设置它。如果不这样做,您希望打印的字符串将从 RAM 的错误部分读取,结果将是乱码。您似乎两次打印相同的乱码这一事实表明了另一个问题,那就是您没有某种无限循环来结束您的引导加载程序。我还有一个 我认为是相关的。
我将填写一个缺少的引导加载程序,它调用您提供的 print_string
函数,该函数使用 0x0000 作为段,我们使用 org 0x7c00
作为起始偏移量:
; Create bootloader with: nasm -f bin boot.asm -o boot.bin
; To test in QEMU use: qemu-system-i386 -fda boot.bin
org 0x7c00
main:
xor ax, ax ; XOR register with itself zeroes the register
mov ds, ax ; We want DS:ORG to be 0x0000:0x7c00
cld ; Forward direction for string instructions like LODSB
mov si,Msg
call print_string ; Print the message
; This is a preferred way to do an infinite loop but
; you could also do: JMP $. This prevents falling through
; the code below and off to memory we don't intend to execute
cli
.endloop:
hlt
loop .endloop
print_string:
Pusha
mov ah,0Eh
xor bx,bx ; Ensure we are writing to page 0
; XOR register with itself zeroes the register
.repeat:
lodsb
cmp al,0
je .done
int 10h
Jmp .repeat
.done:
Popa
Ret
; Place data after the code but before the boot signature.
Msg db "hello",0 ; Nul terminated string
times 510 - ($ - $$) db 0 ; padding with 0 at the end
dw 0xAA55 ; PC boot signature
或者我们可以使用 0x07c0 的段并使用 org 0x0000
(或省略它),它应该仍然有效。代码的开头可能如下所示:
org 0x0000
main:
mov ax, 0x07c0
mov ds, ax ; We want DS:ORG to be 0x07c0:0x0000
cld ; Forward direction for string instructions like LODSB
这是我对两个版本使用 QEMU 得到的输出:
我正在尝试编写自己的 Os 并且我需要一些 print_string 函数来在 sI 中打印字符串 它是这样的
print_string:
Pusha
mov ah,0Eh
.repeat:
lodsb
cmp al,0
je .done
int 10h
Jmp .repeat
.done:
Popa
Ret
但是
中的输出mov si,Msg
call print_string
就像Wn=Wn=
而消息是 "hello"
这不是一个最小的完整示例,但这个问题很常见。即使没有看到其余代码,我猜您在启动时处理器(或模拟处理器)使用的段与汇编代码使用的偏移量不匹配。
对于 16 位 segment:offset addressing,有许多指向相同物理内存地址的段和偏移量的组合。 BIOS 最常用的是 0x07c0:0x0000 和 0x0000:0x0000。 (0x07c0 << 4)+0x0000 = 物理地址 0x07c00。 (0x0000 << 4) + 0x7c00 = 物理地址也为 0x07c00。这个想法是您希望您的 ORG 指令反映您要使用的偏移部分,然后在引导加载程序启动时手动设置 DS 。 NASM 中缺少 ORG 指令(使用 -f bin
构建时)默认为 org 0x0000
.
我有这样的一般引导加载程序提示:
- When the BIOS jumps to your code you can't rely on CS,DS,ES,SS,SP registers having valid or expected values. They should be set up appropriately when your bootloader starts. You can only be guaranteed that your bootloader will be loaded and run from physical address 0x00007c00 and that the boot drive number is loaded into the DL register. [snip]
- The direction flag used by
lodsb
,movsb
etc could be either set or cleared. If the direction flag is set improperly SI/DI registers may be adjusted in the wrong direction. UseSTD
/CLD
to set it to the direction you wish (CLD=forward/STD=backwards). In this case the code assumes forward movement so one should useCLD
. More on this can be found in an instruction set reference
您不能依赖方向 DF 标志(LODSB 需要)和段寄存器(如 DS)作为特定值(或正确值),当您引导加载程序启动。您需要在代码的开头明确设置它。如果不这样做,您希望打印的字符串将从 RAM 的错误部分读取,结果将是乱码。您似乎两次打印相同的乱码这一事实表明了另一个问题,那就是您没有某种无限循环来结束您的引导加载程序。我还有一个
我将填写一个缺少的引导加载程序,它调用您提供的 print_string
函数,该函数使用 0x0000 作为段,我们使用 org 0x7c00
作为起始偏移量:
; Create bootloader with: nasm -f bin boot.asm -o boot.bin
; To test in QEMU use: qemu-system-i386 -fda boot.bin
org 0x7c00
main:
xor ax, ax ; XOR register with itself zeroes the register
mov ds, ax ; We want DS:ORG to be 0x0000:0x7c00
cld ; Forward direction for string instructions like LODSB
mov si,Msg
call print_string ; Print the message
; This is a preferred way to do an infinite loop but
; you could also do: JMP $. This prevents falling through
; the code below and off to memory we don't intend to execute
cli
.endloop:
hlt
loop .endloop
print_string:
Pusha
mov ah,0Eh
xor bx,bx ; Ensure we are writing to page 0
; XOR register with itself zeroes the register
.repeat:
lodsb
cmp al,0
je .done
int 10h
Jmp .repeat
.done:
Popa
Ret
; Place data after the code but before the boot signature.
Msg db "hello",0 ; Nul terminated string
times 510 - ($ - $$) db 0 ; padding with 0 at the end
dw 0xAA55 ; PC boot signature
或者我们可以使用 0x07c0 的段并使用 org 0x0000
(或省略它),它应该仍然有效。代码的开头可能如下所示:
org 0x0000
main:
mov ax, 0x07c0
mov ds, ax ; We want DS:ORG to be 0x07c0:0x0000
cld ; Forward direction for string instructions like LODSB
这是我对两个版本使用 QEMU 得到的输出: