首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >引导程序的第二阶段使用Int 0x10/ah=0x0e打印垃圾

引导程序的第二阶段使用Int 0x10/ah=0x0e打印垃圾
EN

Stack Overflow用户
提问于 2015-12-04 16:30:12
回答 1查看 1.1K关注 0票数 3

我正在努力学习程序集和编写引导加载程序。下面的代码将软盘驱动器的内容加载到内存中并跳转到它(从地址0x1000开始加载)。这段代码应该在屏幕上打印"X“,但出于某种原因,它会打印一个空格。谁能告诉我出什么事了吗?

代码语言:javascript
复制
[bits 16]
jmp reset
reset:          ;Resets floppy drive
    xor ax,ax   ;0 = Reset floppy disk
    mov dl,0        ;Drive 0 is floppy
    int 0x13
    jc reset        ;If carry flag was set, try again

    mov ax,0x1000   ;When we read the sector, we are going to read address 0x1000
    mov es,ax       ;Set ES with 0x1000

floppy:
    mov ah,0x2  ;2 = Read floppy
    mov al,0x11 ;Reading one sector
    mov ch,0x0  ;Track 1 
    mov cl,0x2  ;Sector 2, track 1
    mov dh,0x0  ;Head 1
    mov dl,0x0  ;Drive = 0 (Floppy)
    int 0x13
    jc floppy   ;If carry flag was set, try again
    jmp 0x1000:0000 ;Jump to 0x1000, start of second program

times 510 - ($ - $$) db 0       ;Fill the rest of sector with 0 
dw 0xAA55   ;This is the boot signiture
;---
;--[segment 2]--
mov bx, var
mov ah, 0x0e
mov al, [bx]
int 0x10
jmp $

var:
db 'X'
times 737280 - ($ - $$) db 0
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-12-04 19:20:24

我可以推断您正在使用NASM (或NASM兼容)汇编程序。我不知道您使用什么操作系统来构建引导加载程序,但我将假设Linux或Windows。其他环境也会有点类似。

您应该将引导加载程序分成两部分,以使这更容易。一个是引导加载程序,第二个是在0x1000:0x0000加载的第二个阶段。这使我们能够正确地定义引导加载程序的起始点。引导加载程序将在物理地址0x07c00加载,第二阶段加载在0x10000 ((0x1000<<4+)+0)。我们需要汇编程序为我们的数据和代码正确地生成地址。

我编写了许多StackOverflow答案,描述了我对代码所做的一些更改。其中几个比较相关的问题是:

  • 一般Boot装载机提示,它给出了一般的指导原则和假设,您不想在引导加载器中这样做
  • 信息在访问内存变量时不正确地设置DS和获取垃圾的缺陷。这在一定程度上适用于第二阶段。

如果您对分段:偏移对没有正确的理解,我建议您使用这个文章。我之所以提到这一点,是因为在您的问题和代码中似乎出现了混淆。您似乎认为物理内存地址0x1000与段:偏移对0x1000:0x0000相同。在你的问题中你说:

下面的代码将软盘驱动器的内容加载到内存中并跳转到它(从地址0x1000开始加载)。

在您的代码中有以下一行和注释:

代码语言:javascript
复制
jmp 0x1000:0000 ;Jump to 0x1000, start of second program

如果您查看该链接,您将发现该段:偏移量通过将段左4位(乘以16小数位)移动到物理地址,然后添加偏移量。方程通常以(segment<<4)+偏移形式出现。在您的示例中,0x1000:0x0000是0x1000的段,偏移量为0x0000。使用该方程获取内存中的物理地址(0x1000<<4)+0x0000 = 0x10000 (而不是0x1000)

从您的代码中,不可能知道您是如何与NASM组装的。我提供了一个示例,说明了如何完成该操作,但重要的部分是将引导加载器拆分。假设我们将引导加载程序放在一个名为bootload.asm的文件中

代码语言:javascript
复制
[bits 16]
[ORG 0x7c00]    ; Bootloader starts at physical address 0x07c00

    ; BIOS sets DL to boot drive before jumping to the bootloader

    ; Since we specified an ORG(offset) of 0x7c00 we should make sure that
    ; Data Segment (DS) is set accordingly. The DS:Offset that would work
    ; in this case is DS=0 . That would map to segment:offset 0x0000:0x7c00
    ; which is physical memory address (0x0000<<4)+0x7c00 . We can't rely on
    ; DS being set to what we expect upon jumping to our code so we set it
    ; explicitly
    xor ax, ax
    mov ds, ax        ; DS=0

    cli               ; Turn off interrupts for SS:SP update
                      ; to avoid a problem with buggy 8088 CPUs
    mov ss, ax        ; SS = 0x0000
    mov sp, 0x7c00    ; SP = 0x7c00
                      ; We'll set the stack starting just below
                      ; where the bootloader is at 0x0:0x7c00. The
                      ; stack can be placed anywhere in usable and
                      ; unused RAM.
    sti               ; Turn interrupts back on

reset:                ; Resets floppy drive
    xor ax,ax         ; 0 = Reset floppy disk
    int 0x13
    jc reset          ; If carry flag was set, try again

    mov ax,0x1000     ; When we read the sector, we are going to read address 0x1000
    mov es,ax         ; Set ES with 0x1000

floppy:
    xor bx,bx   ;Ensure that the buffer offset is 0!
    mov ah,0x2  ;2 = Read floppy
    mov al,0x1  ;Reading one sector
    mov ch,0x0  ;Track 1
    mov cl,0x2  ;Sector 2, track 1
    mov dh,0x0  ;Head 1
    int 0x13
    jc floppy   ;If carry flag was set, try again
    jmp 0x1000:0000 ;Jump to 0x1000, start of second program

times 510 - ($ - $$) db 0       ;Fill the rest of sector with 0
dw 0xAA55   ;This is the boot signature

您应该注意到,我删除了这一行:

代码语言:javascript
复制
mov dl,0x0  ;Drive = 0 (Floppy)

这个硬编码引导驱动器到软盘A:。如果您从USB、硬盘驱动器或软盘B引导:您的代码将无法工作,因为在这些情况下驱动器号可能不会为零。BIOS通过用于加载引导加载程序的实际引导驱动器。该值在寄存器DL中。这是BIOS磁盘函数应该使用的值。因为DL已经包含了引导驱动器,所以我们只使用它作为-is。

第二阶段可以这样修改。我假设有一个名为stage2.asm的文件

代码语言:javascript
复制
[BITS 16]
[ORG 0x0000]      ; This code is intended to be loaded starting at 0x1000:0x0000
                  ; Which is physical address 0x10000. ORG represents the offset
                  ; from the beginning of our segment.

; Our bootloader jumped to 0x1000:0x0000 which sets CS=0x1000 and IP=0x0000
; We need to manually set the DS register so it can properly find our variables
; like 'var'

mov ax, cs
mov ds, ax       ; Copy CS to DS (we can't do it directly so we use AX temporarily)

mov bx, var
mov ah, 0x0e
mov al, [bx]
xor bh, bh       ; BH = 0 = Display on text mode page 0
int 0x10
jmp $

var:
db 'X'

我没有试图简化你的代码。这个想法是为了展示如何添加胶水来解决你的问题。这两个文件都使用ORG指令指定了一个起始点。需要组装引导加载器,以便它们在内存地址0x07c00上工作。您正在加载映射到物理地址0x10000的0x1000:0x0000的第二阶段。我们将ORG设置为0x0000,因为远跳jmp 0x1000:0000将设置CS=0x1000和IP=0x0000。因为IP是0x0000,所以我们希望ORG与它相匹配,这样接近内存的引用相对于我们64k段的开头。

这将允许汇编程序为您的变量和代码生成适当的内存引用。因为您在代码中没有正确地执行此操作,所以您的第二阶段是读取var的错误内存位置,随后显示了一个不正确的字符。

一旦分割了这两个文件,就需要使用NASM组装它们,然后将它们放入磁盘映像中。与您的问题不同,我将使用DD构建720 k软盘映像,然后将引导加载程序放在开头(不截断磁盘),然后将第二阶段从扇区开始。这是可以做到的:

代码语言:javascript
复制
# Assemble both components as binary images with NASM
nasm -f bin bootload.asm -o bootload.bin
nasm -f bin stage2.asm -o stage2.bin

# Create a 720k disk image
dd if=/dev/zero of=disk.img bs=1024 count=720

# Place bootload.bin at the beginning of disk.img without truncating
dd if=bootload.bin of=disk.img conv=notrunc

# Place stage2.bin starting at the second 512byte sector and write
# it without truncating the disk image. bs=512 seek=1 will skip the
# first 512 byte sector and start writing stage2.bin there. 
dd if=stage2.bin of=disk.img bs=512 seek=1 conv=notrunc

您可以使用QEMU运行这样的映像,如下所示:

代码语言:javascript
复制
qemu-system-i386 -fda disk.img 

如果使用Windows,并且无法访问DD,则可以对stage2.asm进行此修改

代码语言:javascript
复制
[BITS 16]
[ORG 0x0000]      ; This code is intended to be loaded starting at 0x1000:0x0000
                  ; Which is physical address 0x10000. ORG represents the offset
                  ; from the beginning of our segment.

; Our bootloader jumped to 0x1000:0x0000 which sets CS=0x1000 and IP=0x0000
; We need to manually set the DS register so it can properly find our variables
; like 'var'

mov ax, cs
mov ds, ax       ; Copy CS to DS (we can't do it directly so we use AX temporarily)

mov bx, var
mov ah, 0x0e
mov al, [bx]
xor bh, bh       ; BH = 0 = Display on text mode page 0
int 0x10
jmp $

var:
db 'X'
; Extend the second stage to (720K - 512 bytes) 
; bootload.bin will take up first 512 bytes 
times 737280 - 512 - ($ - $$) db 0

然后使用以下命令组装和构建720 k磁盘映像:

代码语言:javascript
复制
nasm -f bin bootload.asm -o bootload.bin
nasm -f bin stage2.asm -o stage2.bin
copy /b bootload.bin+stage2.bin disk.img

disk.img将是QEMU或Bochs应该可以使用的720 k磁盘映像。disk.img的最终大小应该是737,280字节。

如果要将值从内存地址移动到寄存器,则可以不使用中间寄存器直接执行。在您的stage2.asm中有以下内容:

代码语言:javascript
复制
mov bx, var
mov ah, 0x0e
mov al, [bx]

它可以写成:

代码语言:javascript
复制
mov ah, 0x0e
mov al, [var]

这将从内存位置var移动一个字节,并将其直接移动到AL。NASM将大小确定为一个字节,因为目标AL是一个8位寄存器。

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34092965

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档