INT 10h/ah=13h 在第二阶段引导加载程序的一部分时不打印字符串

INT 10h/ah=13h doesn't print strings when part of second stage bootloader

我用完了第一个扇区的所有内存,现在我想在第二个扇区(第二阶段)存储一个新的变量字符串并打印它。例如:

hello db 'Hello World'

新的字符串应该在另一个扇区中(因为第一个扇区中没有更多的内存)。我在这样的代码中用 INT 13h,ah=2 to read the second disk sector to address 900h:0000. I stored the variable hello in that sector as well as the code to print. It fails to print my string when I use INT 10h/ah=13h 做到了这一点:

mov ax, 7c0h
mov es, ax

mov bp, hello 
mov ah,13h          ; function 13 - write string
mov al,01h          ; attrib in bl, move cursor
mov bl,0bh          ; attribute - magenta
mov cx,30           ; length of string
mov dh,1            ; row to put string
mov dl,4            ; column to put string
int 10h             ; call BIOS service

当变量在第一个扇区时,它打印得很好,但是当我将它存储在第二个扇区时,它不打印,即使我这样做了:

mov ax, 900h
mov es, ax

示例代码:

xchg bx, bx
mov ax, 7c0h
mov ds, ax

sector_2:
mov bx, 900h
mov es, bx
mov bx, 0
mov ah, 2
mov al, 1
mov ch, 0
mov cl, 2
mov dh, 0                     
mov dl, 80h                    
int 13h                        
call 900h:0000

jmp $

times 510 - ($-$$) db 0            ; Fill empty bytes to binary file
dw 0aa55h                          ; Define MAGIC number at byte 512
;;;;;;;;;;;;;;;;;;;;;;;;

sector_2:
mov ax, 900h
mov es, ax      
mov bp, hello
mov ah,13h          ; function 13 - write string
mov al,01h          ; attrib in bl, move cursor
mov bl,0bh          ; attribute - magenta
mov cx,5            ; length of string
mov dh,1            ; row to put string
mov dl,4            ; column to put string
int 10h             ; call BIOS service 

retf
jmp $

hello db 'Hello'
times 1024 - ($-$$) db 0
times 2*8*63*512 - ($-$$) db 0

我认为您的示例代码中存在一些复制和粘贴错误。您写道:

xchg bx, bx
mov ax, 7c0h
mov ds, ax

但我想你的意思是:

xchg bx, bx
mov ax, 7c0h
mov es, ax      ; Int 10h/ah=13h takes string address in ES:BP

您的代码在第一个片段中是正确的。您的示例有两个 sector_2 标签,因此可能会导致 NASM 一些麻烦。我相信您应该只删除代码中第一次出现的标签。

我假设您正在用类似的东西组装您的代码:

nasm -f bin boot.asm -o boot.img

文件名会有所不同,您可以省略 -f bin,因为它是默认的。


由于您的代码中没有明确的 ORG 指令,NASM 假定 org 0h默认。所有绝对内存引用都相对于 0 的偏移量。在您的情况下,这就是您想要的汇编程序文件的第一个扇区(512 字节)。您已将引导加载程序编码为使用 0x7c0 段,您选择的段和原点 0 应指向物理地址 7c00h。在 segment:offset 寻址中,您将有 (7c0h<<4)+0 (其中 0 是 origin/org),它会产生 7c00h.

的正确结果

所以一切都很好,你正确地将扇区读入内存 900h:0h。然后,您通过 call 900h:0000 从引导加载程序的第一阶段对其执行 FAR CALL。这也是对的。

如果这些都是正确的,问题出在哪里?问题是 NASM 不知道您将前 512 个字节之后的代码加载到内存中的另一个位置,并且所使用的 segment:offset 是相对于 0再次(900h:0000h)。它将继续生成相对于引导加载程序开头的绝对地址。

如果您使用 NDISASM 显示从磁盘映像的字节 512 开始生成的代码,您会发现问题:

00000000  B80009            mov ax,0x900
00000003  8EC0              mov es,ax
00000005  BD1A02            mov bp,0x21a
00000008  B413              mov ah,0x13

这是用命令生成的:

ndisasm -e 512 -b16 boot.img

boot.img 是您生成的图像文件的名称。 -e 512 表示跳过反汇编文件的前 512 个字节。我只对输出的前几行感兴趣,特别是:

mov bp,0x21a

0x21ahello的偏移量。但请注意 0x21a 是十进制的 538,这将是相对于整个引导加载程序开头的偏移量,而不是相对于偏移量 0 (900h:0000h) 的偏移量。要解决此问题,您需要指示 NASM 在第二阶段(第二扇区)生成的代码需要相对于原点 0 而不是相对于开头引导加载程序。这可以通过将第二阶段(第二扇区)放在原点(vstart)重置为 0 的新部分中轻松完成。这可以通过在第二阶段的开头放置这样的部分指令来完成:

section stage2, vstart=0h

所以在你的代码中它看起来像:

dw 0aa55h                          ; Define MAGIC number at byte 512
;;;;;;;;;;;;;;;;;;;;;;;;

section stage2, vstart=0h          ; Section name can be anything of your choosing
sector_2:
mov ax, 900h
mov es, ax
mov bp, hello

现在,如果您查看 NDISASM 输出,它看起来像:

00000000  B80009            mov ax,0x900
00000003  8EC0              mov es,ax
00000005  BD1A00            mov bp,0x1a    ; Notice hello offset is 0x1a not 0x21a

@Jester 将 org 0h 放在第二阶段代码(第二扇区)之前的正确轨道上,但每个代码中只能有一个 ORG 指令汇编文件。无论您将它放在文件中的什么位置,NASM 都会像它实际在文件顶部找到它一样起作用。这种行为没有很好的记录! Jester 的解决方案不会改变任何东西。 NASM 的 `SECTION 指令可用于汇编文件中的任何位置以重置原点(在本例中为 0) .

有关 ORGSECTION 指令的更多信息可以在 NASM documentation 中找到。 SECTION 指令和 VSTART 参数记录在第 7.1.3 节 bin 格式的多节支持