在bootsector中使用INT 0x10打印字符串

时间:2015-06-14 19:07:20

标签: assembly x86 fasm

我想创建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

4 个答案:

答案 0 :(得分:2)

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

ORG 0x7c00

start:
    push cs
    pop ds
    mov si, welcome

答案 1 :(得分:2)

根据documentation for BIOS int 0x10:

  

电传打字输出:AH = 0Eh,AL =字符,BH =页码,BL =彩色(仅限图形模式)

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

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

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

答案 2 :(得分:2)

我对此完全陌生,我不确定这是否是做到这一点的有效方法,但它对我有用

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

答案 3 :(得分:1)

如果在您的 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 时我们不应该使用 BX 来寻址字符串 程序!

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

我想要什么

<块引用>

我想创建 printl 函数,允许我在 ax 寄存器中打印字符串。

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

请注意:在代码大小决定一切的一次性引导加载程序代码中,所有这些“削减”都很好。它并不意味着在您的日常编码中使用。