使用诠释为0x10引导程序打印垃圾的第二阶段/啊=为0x0E [英] Second stage of bootloader prints garbage using Int 0x10/ah=0x0e

查看:196
本文介绍了使用诠释为0x10引导程序打印垃圾的第二阶段/啊=为0x0E的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想学习汇编和写入引导程序。下面code加载软盘驱动器内存中的内容,并跳转到它(开始加载地址为0x1000)。这code被认为能打印出X在屏幕上,但由于某些原因,它打印的空间。谁能告诉我什么是错的?

  [位16]
JMP复位
复位:;复位软驱
    异斧,斧; 0 =重置软盘
    MOV DL,0;驱动器0是软盘
    INT 0x13
    JC复位;如果进位标志设置,再试一次    MOV AX,为0x1000,当我们读到的部门,我们要读地址为0x1000
    MOV ES,AX;设置ES与为0x1000软盘:
    MOV啊,0X2; 2 =读取软盘
    MOV等,为0x11;读一个扇区
    MOV CH,为0x0;轨道1
    MOV CL,0X2; 2区,轨道1
    MOV DH,为0x0;头1
    MOV DL,为0x0,驱动器= 0(软盘)
    INT 0x13
    JC软盘;如果进位标志设置,再试一次
    JMP为0x1000:0000;跳转到0x1000处,第二个节目开始次510 - ($ - $$)分贝0; 0填充部门的其余部分
DW 0xAA55将;这是引导signiture
; ---
- [段2] -
MOV BX,VAR
MOV啊,为0x0E
MOV人,[BX]
INT为0x10
JMP $VAR:
分贝X
次737280 - ($ - $$)0分贝


解决方案

我可以推断出你正在​​使用的 NASM 的(或 NASM 的兼容)汇编。我不知道你正在使用构建引导程序是什么操作系统,但我会假设Linux或Windows。其他环境中会有些类似。


您应该引导程序分成两部分,以使它更容易些。一个是引导程序,第二个是你的为0x1000加载第二阶段:0×0000 的。这使我们能够正确定义为我们的bootloader原点。引导程序预计将在物理地址被加载的 0x07c00 的,并在 0x10000的的((0×1000<&4+)+ 0)的第二阶段。我们需要汇编正确生成地址我们的数据和code。

我已经写了许多介绍一些我已经到code所做的更改StackOverflow的答案。一对夫妇更相关的人是:


如果你没有正确的认识的段:偏移的对我推荐这个<一个href=\"https://web.archive.org/web/20150908174936/http://thestarman.pcministry.com/asm/debug/Segments.html\"相对=nofollow>文章。我提起这事,因为似乎是在你的问题,在你的code混乱。你似乎认为物理内存地址为0x1000是一样的段:offset对0x1000中:为0x0000。在你的问题你说:


  

以下code加载软盘驱动器的内容到内存中,并跳转到它(开始加载地址为0x1000)。


在您的code你有这条线和评论:

  JMP为0x1000:0000;跳转到0x1000处,第二个节目开始

如果您查看该链接,你会发现段:由(16小数乘法)移段左4位,然后加偏移计算到物理地址的偏移量。等式通常会出现的(段&下; 4;)+偏移量的。你的情况为0x1000:0×0000是为0x1000段和0x0000的偏移量。用公式来获得内存的物理地址,你会得到点(0x1000&LT;&4;)+ 0×0000 = 0x10000的(不为0x1000)


从code那么它是不可能告诉你如何与 NASM 的组装。我公司提供的是如何做一个例子,但重要的部分是分裂​​的引导程序了。假设我们把你的bootloader的一个名为 bootload.asm

  [位16]
[ORG 0x7c00]引导程序开始于物理地址0x07c00JMP复位
复位:;复位软驱
    ; BIOS设置DL跳跃到bootloader前启动驱动器    ;因为我们指定的0x7c00的ORG(补偿),我们应该确保
    ;数据段(DS)被相应地设置。在DS:胶印,将工作
    ;在这种情况下,是DS = 0。这将映射到段:偏移为0x0000:0x7c00
    ;这是物理存储器地址(0×0000&下; 4;)+ 0x7c00。我们不能依赖
    ; DS被设置为我们所期望的跳时我们code,所以我们把它
    ;明确地
    异斧,斧
    MOV DS,AX; DS = 0    CLI;关闭中断的SS:SP更新
                      ;以避免与手推车8088的CPU的一个问题
    MOV SS,AX; SS = 0×0000
    MOV SP,0x7c00; SP = 0x7c00
                      ;我们将设置堆栈开始的正下方
                      ;其中引导程序是为0x0:0x7c00。该
                      ;栈可以在任何地方使用的放置和
                      ;未使用的RAM。
    STI;打开中断回    异斧,斧; 0 =重置软盘
    INT 0x13
    JC复位;如果进位标志设置,再试一次    MOV AX,为0x1000,当我们读到的部门,我们要读地址为0x1000
    MOV ES,AX;设置ES与为0x1000软盘:
    异或BX,BX;确保缓冲区偏移为0!
    MOV啊,0X2; 2 =读取软盘
    MOV等,为0x1;读一个扇区
    MOV CH,为0x0;轨道1
    MOV CL,0X2; 2区,轨道1
    MOV DH,为0x0;头1
    INT 0x13
    JC软盘;如果进位标志设置,再试一次
    JMP为0x1000:0000;跳转到0x1000处,第二个节目开始次510 - ($ - $$)分贝0; 0填充部门的其余部分
DW 0xAA55将;这是引导签名

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

  MOV DL,为0x0,驱动器= 0(软盘)

这很难codeS启动驱动器到软盘A :.如果您开机关机USB,硬盘或软盘的B:你的code将无法工作,因为驱动器号可能不会在这种情况下为零。该BIOS传递被用来加载你的bootloader的实际引导驱动器。该值在寄存器中的 DL 的。这是你应该使用的值BIOS磁盘功能。由于的 DL 的已经包含了引导驱动器,我们只是用它原样。


第二阶段可以以这种方式被修改。我假设一个名为 stage2.asm

  [位16]
[ORG为0x0000];这code打算开始为0x1000加载:为0x0000
                  ;这是物理地址0x10000处。 ORG重新presents偏移
                  ;从我们的段的开始。;我们的bootloader跃升为0x1000处:为0x0000这台CS = 0×1000和IP = 0×0000
;我们需要手动设置DS注册,因此它可以准确地找到我们的变量
;像变种MOV AX,CS
MOV DS,AX; CS复制到DS(我们不能做到这一点直接,所以我们暂时用AX)MOV BX,VAR
MOV啊,为0x0E
MOV人,[BX]
异或BH,BH; BH = 0 =文本模式页0显示
INT为0x10
JMP $VAR:
分贝X

我没有尝试简化您的code。这样做是为了显示如何添加胶水解决您的问题。这两个文件中指定使用 ORG 指令的原点。引导程序需要进行组装,使它们在存储器地址0x07c00工作。您正在加载的第二阶段位于0x1000:为0x0000映射到物理地址0x10000处。我们设置ORG为0x0000以来的 FAR JUMP JMP为0x1000:0000 将设置的 CS = 0x1000处,而 IP 的= 0×0000。因为<青霉> IP 的是0×0000,我们希望ORG匹配它使邻近存储器引用是相对于我们64k的段的开始。

这将使汇编生成变量和code正确的内存引用。因为你没有正确地做到这一点在你的code,你的第二个阶段是读错的内存位置 VAR ,随后显示不正确的字符。


一旦你有2个文件分割您需要的 NASM 的组装它们,然后将它们放入一个磁盘映像。不像你的问题,我将使用的 DD 的构建720K软盘映像,然后将在开始引导装载程序(不截断盘),然后把之后的部门开始的第二阶段。这可以这样实现:

 #组装部件都与NASM二进制图像
NASM -f斌bootload.asm -o bootload.bin
NASM -f斌stage2.asm -o stage2.bin#创建一个720K磁盘映像
DD如果= /开发/的= disk.img BS = 1024计数= 720为零在disk.img年初#广场bootload.bin不截断
DD如果= bootload.bin = disk.img兑换= notrunc之外#将stage2.bin开始在第二个512字节扇区和写入
#它不截断磁盘映像。 BS = 512 =觅1将跳过
#第一个512字节扇区,并开始写stage2.bin那里。
DD如果= stage2.bin = disk.img BS = 512寻求= 1 =单次转​​换notrunc之外

您可以与像使用运行QEMU这样一个形象:

 的qemu-系统的i386 -FDA disk.img


如果使用的是Windows,而不必访问的 DD 的,您可以使用此修改 stage2.asm

  [位16]
[ORG为0x0000];这code打算开始为0x1000加载:为0x0000
                  ;这是物理地址0x10000处。 ORG重新presents偏移
                  ;从我们的段的开始。;我们的bootloader跃升为0x1000处:为0x0000这台CS = 0×1000和IP = 0×0000
;我们需要手动设置DS注册,因此它可以准确地找到我们的变量
;像变种MOV AX,CS
MOV DS,AX; CS复制到DS(我们不能做到这一点直接,所以我们暂时用AX)MOV BX,VAR
MOV啊,为0x0E
MOV人,[BX]
异或BH,BH; BH = 0 =文本模式页0显示
INT为0x10
JMP $VAR:
分贝X
;延伸所述第二阶段(720K - 512字节)
; bootload.bin将占据前512个字节
次737280 - 512 - ($ - $$)0分贝

和再组装和使用这些命令建立720K磁盘映像:

  NASM -f斌bootload.asm -o bootload.bin
NASM -f斌stage2.asm -o stage2.bin
复制/ B bootload.bin + stage2.bin disk.img

disk.img 将是这应该是由QEMU或Bochs的可使用的720K磁盘映像。的最终大小 disk.img 应该是737280字节。


如果您想从一个内存地址到寄存器移动值,可以直接做没有中间寄存器。在你的 stage2.asm 你有这样的:

  MOV BX,VAR
MOV啊,为0x0E
MOV人,[BX]

它可以被写成:

  MOV啊,为0x0E
MOV人,[VAR]

这会从内存位置的 VAR 的移动单个字节,并直接进入它的 AL 的。大小由 NASM 的,因为目标的 AL 的是一个8位寄存器是一个字节决定的。

I am trying to learn assembly and to write a bootloader. The following code loads the contents of a floppy drive to memory and jumps to it (starts loading at address 0x1000). This code is supposed to print "X" on the screen, but for some reason it prints a space. Can somebody please tell me what is wrong?

[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

解决方案

I can deduce you are using NASM (or NASM compatible) assembler. I don't know what OS you are using to build the bootloader, but I'll assume Linux or Windows. Other environments would be somewhat similar.


You should split your bootloader into two parts to make this easier. One is the bootloader, and the second being the second stage you load at 0x1000:0x0000. This allows us to properly define the origin point for our bootloader. The bootloader is expected to be loaded at physical address 0x07c00, and the second stage at 0x10000 ((0x1000<<4+)+0). We need the assembler to properly generate addresses for our data and code.

I have written a number of StackOverflow answers that describe some of the changes I have made to the code. A couple of the more relevant ones are:

  • General Boot Loader Tips which give general guidelines and assumptions you don't want to make in a bootloader
  • Information on the pitfalls of not setting up DS properly and getting garbage when accessing memory variables. This applies somewhat to your second stage

If you don't have a proper understanding of segment:offset pairs I recommend this article. I bring this up because there seems to be confusion in your question and in your code. You seem to think that physical memory address 0x1000 is the same as the segment:offset pair 0x1000:0x0000. In your question you say:

The following code loads the contents of a floppy drive to memory and jumps to it (starts loading at address 0x1000).

In your code you have this line and comment:

jmp 0x1000:0000 ;Jump to 0x1000, start of second program

If you review that link you'll discover that segment:offset computes to a physical address by shifting the segment left 4 bits (multiply by 16 decimal) and then adding the offset. The equation usually appears as (segment<<4)+offset . In your case 0x1000:0x0000 is a segment of 0x1000 and offset of 0x0000 . Using the equation to get the physical address in memory you'd get (0x1000<<4)+0x0000 = 0x10000 (not 0x1000)


From your code it isn't possible to tell how you are assembling with NASM. I provide an example of how it could be done, but the important part is splitting the bootloader up. Assume we put your bootloader in a file called bootload.asm:

[bits 16]
[ORG 0x7c00]    ; Bootloader starts at physical address 0x07c00

jmp reset
reset:          ;Resets floppy drive
    ; 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

    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

You should notice that I removed this line:

mov dl,0x0  ;Drive = 0 (Floppy)

This hard codes the boot drive to the Floppy A:. If you boot off of USB, hard drive, or Floppy B: your code won't work because the drive number likely won't be zero in those cases. The BIOS passes the actual boot drive that was used to load your bootloader. That value is in the register DL. This is the value you should be using for BIOS disk functions. Since DL already contains the boot drive, we just use it as-is.


The second stage can be modified in this way. I'll assume a file called stage2.asm:

[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'

I've made no attempt to streamline your code. The idea is to show how to add the glue to fix your issues. Both files specify an origin point using the ORG directive. Bootloaders need to be assembled so that they work at memory address 0x07c00 . You are loading the second stage at 0x1000:0x0000 that maps to physical address 0x10000. We set ORG to 0x0000 since the FAR JUMP jmp 0x1000:0000 will set CS=0x1000, and IP=0x0000 . Because IP is 0x0000 we want ORG to match it so that near memory references are relative to the beginning of our 64k segment.

This will allow the assembler to generate proper memory references for your variables and code. Because you didn't properly do this in your code, your second stage was reading the wrong memory location for var and subsequently displayed an incorrect character.


Once you have the 2 files split you need to assemble them with NASM and then place them into a disk image. Unlike your question, I will use DD to build a 720k floppy disk image and then place the bootloader at the beginning (without truncating the disk) and then place the second stage starting at the sector right after. That can be accomplished like this:

# 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

You could run such an image using QEMU with something like:

qemu-system-i386 -fda disk.img 


If using Windows, and you don't have access to DD, you may be able to use this modification to stage2.asm:

[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

And then assemble and build the 720K disk image with these commands:

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 would be the 720K disk image that should be usable by QEMU or Bochs. The final size of disk.img should be 737,280 bytes.


If you want to move a value from a memory address to a register, you can do it directly without an intermediate register. In your stage2.asm you have this:

mov bx, var
mov ah, 0x0e
mov al, [bx]

It could be written as:

mov ah, 0x0e
mov al, [var]

This would move a single byte from the memory location var and move it directly to AL . The size is determined by NASM to be a byte because the destination AL is an 8-bit register.

这篇关于使用诠释为0x10引导程序打印垃圾的第二阶段/啊=为0x0E的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆