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

时间:2016-06-28 09:38:58

标签: assembly x86 nasm x86-16 bootloader

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

hello db 'Hello World'

新字符串应该在另一个扇区中(因为第一个扇区中没有更多内存)。我使用INT 13h,ah=2执行此操作以读取第二个磁盘扇区以解决900h:0000。我将变量hello存储在该扇区中以及要打印的代码中。当我在这样的代码中使用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

1 个答案:

答案 0 :(得分:7)

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

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<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<&quot

所有一切都很好,你在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指令来完成: / p>

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 都会像在文件顶部找到它一样。这种行为没有很好的记录!杰斯特的解决方案不会改变任何事情。可以在汇编文件中的任何位置使用 NASM SECTION 指令来重置原点(在本例中为0)。

有关 ORG SECTION 指令的更多信息,请参阅NASM documentation SECTION 指令和 VSTART 参数在7.1.3节对bin格式的多节支持

中进行了说明。