在引导扇区中使用 INT 0x10 打印字符串

Print string using INT 0x10 in bootsector

我想创建 printl 函数,允许我在 ax 寄存器中打印字符串。我在 16 位实模式下,找不到任何打印消息的方法。我使用 int 0x10 打印单个字母。

我尝试在 bx 寄存器中传递参数(要打印的字符串),然后在循环中逐个字母打印,然后返回使用 poparet。我的代码并没有真正起作用——它要么创建了一个无限循环,要么打印了一个奇怪的符号。

如果您知道更有效的方法,那么这不是问题。我还想问一下评论你的代码,如果你给了

这是我的代码

boot.asm:

start:
    mov bx, welcome    ;put argument to bx
    call printl        ;call printl function in sysf.asm
    hlt                ;halt cpu

welcome db 'Hello', 0

include 'sysf.asm'
times 510 - ($-$$) db 0

db 0x55
db 0xAA

sysf.asm:

;print function
; al is one letter argument (type Java:char)
;
print:
        pusha
        mov ah, 0x0e
        int 0x10
        popa
        ret              ; go back

;printl function
; bx is argument of type Java:String
;
printl:
        pusha
        jmp printl001
printl001:
        lodsb             ; I was working with si register but i would like to use bx register
        or al,al
        jz printl002
        mov ah, 0x0e
        int 0x10
        jmp printl001 
printl002:
        popa
        ret

lodsb 指令加载 DS 和 SI 寄存器指向的字节,但您还没有加载有效值。由于这是一个引导加载程序,您还需要使用 ORG 指令,否则汇编程序将不知道您的代码在哪里,因此 welcome 消息将被加载到内存中。尝试将程序的开头更改为:

ORG 0x7c00

start:
    push cs
    pop ds
    mov si, welcome

根据documentation for BIOS int 0x10:

Teletype output: AH=0Eh, AL = Character, BH = Page Number, BL = Color (only in graphic mode)

如果BH不为零,则写入不显示的视频页面。当然,除非您已翻转以显示 BH 中的任何页面。可能你会想要修改你的打印功能:

print:
        pusha
        mov ah, 0x0e
        xor bx, bx       ; BX = 0
        int 0x10
        popa
        ret              ; go back

如果您的输出导致屏幕滚动,BP might be destroyed,尽管它不会对您的代码造成问题,因为它保留了所有寄存器。

我对此完全陌生,我不确定这是否是执行此操作的有效方法,但它对我有用

print_string:
    pusha
    mov ah, 0x0e
    mov al, [bx]
    loop:
        cmp al, 0
        je break
        int 0x10
        add bx, 0x01
        mov al, [bx]
        jmp loop
    break:
        popa
        ret

如果在你的 sysf.asm 中你已经有一个字符 print 例程,你为什么不调用它来自你的 printl 例程?
这并不重要,所以请继续阅读...

编写引导加载程序

BIOS 在内存中的地址 7C00h 加载您的引导加载程序,并且 BIOS 传递给您的代码的唯一寄存器是 DL 寄存器中的 BootDrive 编号。没有任何其他寄存器是您可以信赖的,可以保存您希望在那里找到的任何值!

现在,如果您不在引导加载程序代码中使用 ORG 指令,那么汇编程序 (FASM) 将断定您需要隐式 ORG 0。您需要相应地设置段寄存器。 PrintString 过程中的代码取决于 DS 段寄存器。它的正确值为 07C0h。
但是,大多数人更喜欢使用显式 ORG 7C00h 启动引导加载程序代码(如果只是为了使其能够立即识别)。在这种情况下,DS 段寄存器需要加载 0000h。

使用 BIOS api

BIOS.Teletype 函数需要以下参数:

  • BL 图形颜色;这仅在显示处于图形模式时使用
  • BH 显示页面;显示页数取决于视频模式
  • AL 字符代码;要显示的字符的 ASCII 码
  • AH 函数编号;数字 0Eh (0x0E)

因为BLBH寄存器是BX寄存器的一部分,所以在写这种PrintString 程序!

并且因为在引导加载程序中,显示器通常处于文本视频模式的显示页 0,我们可以省略 BL GraphicsColor 参数,但我们仍应设置 BH 显示页面。如果我们不这样做,那么这些字符可能会出现在任何替代显示页面上,甚至根本不会出现在任何地方。

我想要什么

I want to create printl function that allow me to print string in the ax register.

printl这样的名字很难读懂!还有printl001这样的标签真的很伤眼!最好使用像 PrintString 这样的东西来表达它的目的。

AX 寄存器中传递参数没有任何好处。 SI 通常是引导加载程序代码中的最佳选择,因为它有助于使用 lodsb 字符串原语指令。这可以减少代码的足迹。但请注意,明智的做法是使用一次 cld 指令以确保方向标志被重置,以便 SI 可以按照我们想要的方式递增。

        ORG     7C00h

        xor     ax, ax
        mov     ds, ax
        cld                 ; So `lodsb` will increment SI

        mov     si, welcome
        call    PrintString
        hlt
; --------------------------
; IN (si) OUT ()
PrintString:
        pusha               ; Preserving all registers
        mov     bh, 0       ; DisplayPage
        jmp     .While
.Do:    mov     ah, 0Eh     ; BIOS.Teletype
        int     10h
.While: lodsb
        test    al, al      ; Test for the end of the zero-terminated string
        jnz     .Do         ; Not yet
        popa
        ret
; --------------------------
welcome db 'Hello', 0
; --------------------------
times 510 - ($-$$) db 0
dw 0xAA55

因为此代码必须 运行 在 512 字节中,所以使用 1 字节指令 pusha/popa 保留多个寄存器是有意义的。但是,如果程序允许,不保留也会削减这 2 个字节。

下面是另一种传递字符串的方法,它允许不必明确指定字符串的地址,从而减少了 3 个字节。

        ORG     7C00h

        xor     ax, ax
        mov     ds, ax
        cld                 ; So `lodsb` will increment SI

        call    PrintString ; -> (AX BX SI)
        db      'Hello', 0
        hlt
; --------------------------
; IN () OUT () MOD (ax,bx,si)
PrintString:
        pop     si          ; -> SI is the address of the message 'Hello', 0
        mov     bh, 0       ; DisplayPage
        jmp     .While
.Do:    mov     ah, 0Eh     ; BIOS.Teletype
        int     10h
.While: lodsb
        test    al, al      ; Test for the end of the zero-terminated string
        jnz     .Do         ; Not yet
        push    si          ; SI holds the address where execution resumes (here its `hlt`)
        ret
; --------------------------
times 510 - ($-$$) db 0
dw 0xAA55

请注意:在代码大小就是一切的一次性引导加载程序代码中,所有这些“剃掉”都很好。它不适用于您的日常编码。