您好我试图显示实际时间小时/分钟/秒这是我的代码示例:
MOV AH, 2Ch
INT 21h
MOV AH, 0Eh
MOV AL, CH
INT 10h
MOV AL, 3Ah
INT 10h
MOV AL, CL
INT 10h
MOV AL, 3Ah
INT 10h
MOV AL, DH
INT 10h
ret
在这里您可以查看控制台显示的内容
您需要一个打印例程将字节打印为数字,而不是将它们作为字符直接写入屏幕。幸运的是,您只需要处理 0 到 59 之间的值,而且您需要前导零,因此问题非常简单。假设要在 AX 中打印的值:
print2Digits:
;; input in AX (0-99)
;; clobbers AX and DX, save them if needed
MOV DL, 0Ah ; divide by: 10
DIV DL ; first digit in AL (quotient), second digit in AH (remainder)
MOV DX, AX ; save the digits
ADD AL, 30h ; ASCII '0'
MOV AH, 0Eh ; set up print
INT 10h ; print first digit.
MOV AL, DH ; retrieve second digit
ADD AL, 30h
INT 10h ; print it
RET
请参阅x86标签wiki以获取指令集参考手册,以及许多参考资料和教程的良好链接。
将整数拆分为 ASCII 数字需要足够的代码,您应该将其分解为函数。
这是@hobbs 的 print2Digits 函数的优化和修正版本。(我还在他的答案中修正了版本,所以它也是正确的,但留下了这个优化)。
print2Digits:
;; input in AL (0-99). (Or preferably already zero-extended to AX so we can omit CBW)
;; clobbers AX and DX
cbw ; zero AH. Sign-extending AL does the job because AL is only allowed to be 0-99.
mov dl, 10
div dl ; quotient in AL(first (high) digit), remainder in AH(second (low) digit)
add ax, 0x3030 ; add '0' to al and ah at the same time.
mov dl, ah ; save the 2nd digit
mov ah, 0x0E ; BIOS call #: print single character
int 0x10 ; print high digit first. Doesn't clobber anything, so AH still holds 0x0E after
mov al, dl
int 0x10 ; print the low digit 2nd
ret
由于我们曾经div
将一个整数拆分为两个 base10 数字,因此我们需要ah
为零。即股息在 AX 中,而不仅仅是在 AH 中可能存在垃圾的 AL。我们可以将cbw
or mov ah,0
if the caller did movzx ax, ch
or something 保存为零ah
。
(除了 8086 没有movzx
,所以你实际上想要xor ax,ax
/ mov al, ch
。)
有一个用于打印整个字符串的 DOS 系统调用,因此您可以将字符存储到一个小缓冲区中并一次打印它们,就像我在这个AMD64 Linux FizzBuzz中所做的那样。另请参阅如何在没有来自 c 库的 printf 的情况下在汇编级编程中打印整数?对于缓冲区函数中更通用的 int->string,或x86 标签 wiki中的其他多位数字链接
也可以使用aam
将 AL(而不是 AX)除以 10,从而避免需要先将 AH 归零。它比div r8
当前的 Intel 和 AMD CPU 稍快。但是,它将结果放在与 相对的寄存器中div
,这意味着 . 之后有额外的指令aam
。这平衡了 和 的mov dl, 10
节省cbw
。
print2Digits:
;; input in AL (0-99). (Ignores AH because we use AAM instead of div)
;; clobbers AX and DX
aam ; like `div` by 10, but with the outputs reversed, and input from AL only
;; quotient in AH (high digit), remainder in AL(low digit). (Opposite to div)
add ax, 0x3030 ; add '0' to al and ah at the same time.
mov dl, al ; save the low digit
mov al, ah ; print high digit first
mov ah, 0x0E ; BIOS call #: print single character
int 0x10 ; print first digit. Doesn't clobber anything, so AH still holds 0x0E after
mov al, dl
int 0x10 ; print second digit
ret
即使我们想存储到一个字符串(并调用一个打印字符串函数或系统调用),我们也必须在将 AX 存储到内存之前交换 al 和 ah(例如xchg al,ah
,或者在现代硬件上更有效,但需要186:)rol ax,8
。 div
在 AX 中以正确的顺序生成它们。
对于 32bit 地址大小可用的 386,我们可以节省一条指令:
lea dx, [eax + 0x3030] ; need a 32bit addressing mode to use eax as a source reg. Adds '0' to both digits at once, with a different destination.
mov al, dh ; then get ready to print the high byte first
需要一个地址大小的lea
前缀和一个 2 字节的 mod/rm,以及一个 32 位的位移,所以它在代码大小上损失惨重,但它确实节省了一条指令。
在 Sandybridge 系列 CPU 上,尤其是在写入后使用lea
读取可能会更快。Haswell 和更高版本,但在 Intel pre-SnB 上,部分寄存器停顿将使其更好地使用具有单独 add 和 mov 指令的纯 16 位版本。eax
div
ax
当然,如果您真的关心性能,您会使用乘法逆而不是实际除以 10。 而且您通常也不会编写进行传统 BIOS 调用的 16 位代码!
您试图在 中显示值(时间)CH
,CL
并将DH
其注册为 aschi 字符。
假设您CH
包含值 15h。
现在,当您将此值放入AL
并调用int 10h
时,它会显示与值 15h 匹配的 aschi 字符。如果你想显示 15h 的十进制数字,那么你将不得不调用一个显示例程,它会破坏寄存器中的值并提取其中的数字来显示 - 而不仅仅是该值中的 aschi 表示。
假设您在寄存器中有十进制 85。现在您需要在屏幕“8”和“5”上打印。所以你必须将值 85 转换为 aschi-56("8") 和 aschii-53("5")。然后将它们依次放入寄存器,调用int 10
两次显示“85”。
霍布斯的回答显示了如何做到这一点。
这是另一个教程