我想输出RTC日期。
我意识到我必须将BCD转换为ASCII才能做到这一点,但我不知道该怎么办。
要转换,我使用此页面:BCD to ASCII conversion,但它使用中断21h并在此页面中:https://en.wikipedia.org/wiki/BIOS_interrupt_call中断21h不存在。
如何转换和打印BCD?
DAY:
mov ah,0x04
int 0x1a
mov al,dl
aam ;to convert.
mov bx,ax
mov cx,ax
add dl,ch
mov ah,02h
int 21h
mov dl,cl
int 21h
我在ubuntu中测试了代码。我想我应该使用中断10h输出而不是21h。 (int 21h
不是有效)。
那么,如何将BCD转换为ASCII,以及如何使用中断10h将其打印? 只需使用以下风格?
mov al, 'h'
int 10h
答案 0 :(得分:4)
什么是BCD (binary-coded decimal)?
在BIOS时间中断结果的情况下,"打包的BCD"用来。每个字节(8位)被分成两半的4位(半字节),每个包含一个十进制值(4位允许0-15个值,但只有0-9个值用于保持"十进制&#34 ;)
因此,例如,如果时间以" 37"结束。秒,秒字节将包含值0x37 = 55 = 0b00110111
。注意在十六进制和二进制两者中,即使是人类在短暂练习之后也是可见/可读的(在十进制中,你必须在头部计算除法/余数16以将其分成3和7:{{1 },55 / 16 = 3
)。
现在汇编非常简单,因为即使您不想深入研究像AAM
这样的专用BCD指令,也可以使用普通的位掩码+移位来提取这两个值。
我们假设您在55 % 16 = 7
中有价值0x37
,并且您希望将其提取到BL
(BH
)和0x03
( BL
),然后例如此代码将计算:
0x07
如何显示它?
这取决于您的目标平台,从问题和评论来判断您的目标是PC兼容计算机上只有BIOS可用的x86 16b实模式,因此最简单的方法之一是将这些十进制0-9值转换为ASCII值数字(添加48个作品,或许多汇编程序将为您进行字符转换,因此例如mov bh, bl ; copy the packed value also into BH
and bl, 0x0F ; keep only lower 4 bits in BL (0x37 & 0x0F = 0x07)
shr bh, 4 ; shift right by 4 bits BH (unsigned(0x37) >> 4 = 0x03)
; and that's it, it's that simple to manipulate bits in assembly
有效,您可以将其视为"将字体字符0添加到0-9值&#34 ;,并且由于字体中的下一个数字是以add bl,'0'
的+1方式定义的,因此它将计算正确的字符值。
然后使用其中一个int 10h
服务来显示ASCII字符,就像'0'
服务一样。从AH=0x0A
显示ASCII字符的示例:
BL
由于您想要显示时间,并且您有多个寄存器中的值,您可能需要在打印之间保留它们,例如,如果我使用int 0x1A,02
服务来读取RTC,我会使用这个"架构"显示它(不是编写完整的实现,只是顶部逻辑,向您展示如何使用mov ah, 0x0A ; write ASCII character
mov al, bl ; character value to write
xor bx, bx ; bh = bl = 0 in text mode (adjust in gfx mode as needed)
mov cx, 1 ; write it only once
int 0x10 ; call the BIOS service
堆栈指令保留值供以后使用):
push/pop
现在我将在评论中评论你的一些问题:
read_and_display_rtc_time:
mov ah,2
int 0x1A ; call service 00 of RTC BIOS interrupt
push dx ; preserve DH (seconds)
push cx ; preserve CL (minutes)
mov al,ch ; AL = CH (hours)
call display_bcd ; display AL as BCD-packed decimal value
call display_colon ; display ":"
pop ax ; restore minutes (push cx) into AL
call display_bcd ; display AL as BCD-packed decimal value
call display_colon ; display ":"
pop ax
mov al,ah ; restore seconds into AL
call display_bcd ; display AL as BCD-packed decimal value
ret
display_bcd:
; AL contains BCD-packed decimal value (4:4 bits)
; TODO code to split it into two values, convert to ASCII and display
ret
display_colon:
; TODO code to display colon on screen
ret
是的,这是不合法的,因为mov cl,ax
是16位寄存器,而ax
是8位寄存器(cl
的低8b部分的别名),所以你在这种操作中会丢失8位。
要向程序员显示你确实想要丢失高8位,你可以写cx
(mov cl,al
是{{1}的低8位的别名}})。
像al
这样的相反转换将再次失败,但作为程序员,您可以精确定义,如何将8位值转换为16位值。
例如,做" unsigned"转换你可以使用386+指令:
ax
或8086-80286计算方式:
mov ax,cl
并且"签署"那里的转换又是专门的386+方式:
movzx ax,cl
请记住计算机是一个更复杂的计算器,没有别的,所以你应该尝试从单词任务转换为思考输入端有哪些数值,哪些数值代表输出端(偶数& #34;字符"只是计算机中的数值),然后您只需计算数学公式即可将输入数字转换为输出数字。
如果您有bochs,请注释掉尚未编译的代码,并从可用的小块代码开始,检查调试器正在执行的操作,这样您就可以直观地看到计算的进行情况,然后慢慢添加2-3个指令的新代码部分,并在调试器中继续运行它们以查看它们是否计算出您想要的内容。 (过度使用单词"计算"在最近的段落中是有意的..如果你知道如何操作计算器,你非常接近知道如何在汇编中编码,虽然你的初始来源可能很难看,因为它还需要时间来提升"样式和效率 - 只需检查指令集以了解可能的计算类型,并更好地了解xor ax,ax ; set all bits of AX to zero first
mov al,cl ; copy the CL into lower 8 bits of AX
是16位寄存器的含义,并且movsx ax,cl
; or before 80386 times there are two common options
mov al,cl ; if "ax" is target, then there's special instr.
cbw ; sign extend AL into AX
; or when non-ax register is target, like DX
mov dh,cl ; copy CL into upper 8 bits first
sar dx,8 ; now do sign-right-shift by 8 bits
; dx = sign extended CL
是8位,以及如何以位(电流)编码数值。
"在ubuntu"
中测试
不完全是,你在ubuntu中只运行AX
来生成16位x86机器代码二进制文件(交叉编译,你可以在任何其他机器上生成这样的二进制文件,它有x86汇编程序,与你的问题不太相关,除了你应该指定NASM,所以试图回答你的人会知道要使用哪种x86汇编语法,每个汇编器都有微小的差异)。然后你在虚拟机(BOCHS)中尝试了这个二进制文件。如果您尝试直接在ubuntu下运行它,它将完全失败,因为ubuntu OS不支持16b实模式机器代码的运行时环境。
如果不确定相关内容,只需描述您使用的所有工具和命令行/选项。