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
0x21a
是hello
的偏移量。但请注意 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) .
有关 ORG 和 SECTION 指令的更多信息可以在 NASM documentation 中找到。 SECTION 指令和 VSTART 参数记录在第 7.1.3 节 bin 格式的多节支持
我用完了第一个扇区的所有内存,现在我想在第二个扇区(第二阶段)存储一个新的变量字符串并打印它。例如:
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
0x21a
是hello
的偏移量。但请注意 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) .
有关 ORG 和 SECTION 指令的更多信息可以在 NASM documentation 中找到。 SECTION 指令和 VSTART 参数记录在第 7.1.3 节 bin 格式的多节支持