如何在实模式汇编中修复这个整数输出子程序?
我正在编写一个引导加载程序(第二阶段,那里有足够的空间放置这段代码),并且在 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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。