如何在实模式汇编中修复这个整数输出子程序?

编程语言 2026-07-09

我正在编写一个引导加载程序(第二阶段,那里有足够的空间放置这段代码),并且在 print_int 例程上遇到了问题。

我使用QEMU(i386)作为虚拟机,NASM作为汇编器。用于汇编的命令:nasm -f bin kernel_init.asm -o kernel_init.bin

不知何故,这段代码就是不起作用。当我尝试打印数字 5 时:

mov si, 5
mov di, 10
call print_int

它输出了 6553。当我尝试 17 时,它打印了 6551。输入 728 时,打印 6480。 我相信问题出在将数字按基数除法并存储余数的代码,或者在把缓冲区反向输出的代码。

; Displays a single character to the screen
; void putc(char %al)
putc:
    mov ah, 0Eh
    int 10h

    ret

; Prints an integer to the screen
; void print_int(num %si, base %di)
print_int:
    ; Initialize stack frame
    push bp
    mov bp, sp

    cmp di, 2
    jl .return

    cmp di, 36
    jg .return

    ; Store %bx and %si
    push bx
    push si

    ; Reserve variables
    sub sp, BUFFER_SIZE
    xor cx, cx
    push 0

    ; Handle negatives
    cmp si, 0
    jl .negative

    .negative:
        ; If not base 10, skip
        cmp di, 10
        jnz .loop

        ; Base 10
        neg si
        mov bx, sp
        mov [bx], word 1

    .loop:
        ; Get remainder (%dx)
        xor dx, dx
        mov ax, si
        div di

        ; Get offset in the buffer
        mov bx, sp
        add bx, cx
        add bx, 2

        ; Store remainder
        push si
        mov si, digits
        add si, dx

        push ax

        mov al, [si]
        mov [bx], al

        pop ax
        add sp, 2

        ; Continue loop
        mov si, ax

        inc cx

        cmp si, 0
        jle .finish

        jmp .loop

    .finish:
        dec cx

        mov bx, sp
        add bx, 2

        cmp di, 10
        jz .print_minus

    .print_number:
        .print_loop:
            cmp cx, 0
            jle .end_print

            add bx, cx
            mov al, [bx]
            sub bx, cx

            dec cx

            call putc
            jmp .print_loop

        .end_print:
            jmp .return

    .print_minus:
        push bx
        mov bx, sp
        add bx, 4

        mov ax, [bx]
        pop bx

        cmp ax, 1
        jnz .print_number

        mov al, '-'
        call putc

        jmp .print_number

    .return:
        mov sp, bp
        pop bp

        ret

digits: db "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
BUFFER_SIZE: equ 64

解决方案

不知何故,这段代码就是不起作用。

``` cmp si, 0 jl .negative

.negative: ```

这不能起作用!跳转到穿透 进入 .negative 将把每个值都视为负数。快速修复的方法是把条件取反,并把 jl .negative 替换为 jge .loop。但你为什么要保留你当前那套相当复杂的解法呢?

第二阶段,里面有足够的空间来放置那段代码

确实如此,但浪费字节从来不是好主意……

既然你是逐字符输出结果,有一种更好的解决办法,不需要建立缓冲区(也省去处理它的麻烦)。此外,你还可以避免维护负号标志的复杂性,因为我们可以直接输出字符。在DOS中显示数字 有更多关于如何显示数字的信息。它提到DOS并不重要,因为你完全可以把DOS换成BIOS Teletype,这正是我在这里所做的。

下面是我建议的一个更小更清晰版本:

; Prints an integer to the screen
; IN (si,di) OUT () MOD (ax,dx)
print_int:
      push si       ; SI is integer number
      cmp  di, 2    ; DI is base [2,36]
      jl   .RET
      cmp  di, 36
      jg   .RET

      cmp  di, 10
      jne  .NUM     ; Always positive for non-base 10
      test si, si
      jns  .NUM
.NEG: neg  si
      mov  ax, 0Eh<<8 + '-'
      int  10h      ; BIOS.Teletype (putc)

.NUM: mov  ax, si
      push di       ; Sentinel
.DIV: xor  dx, dx
      div  di
      push dx       ; Remainder [0,35]
      test ax, ax
      jnz  .DIV

      pop  si       ; First pop is a remainder for sure
.POP: mov  al, [digits + si]
      mov  ah, 0Eh
      int  10h      ; BIOS.Teletype (putc)
      pop  si
      cmp  si, di
      jb   .POP     ; Keep popping until we pop the sentinel
.RET: pop  si
      ret

digits: db "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章