如何将BCD转换为ASCII并打印结果?

时间:2017-09-09 18:18:08

标签: ubuntu assembly bios bcd

我想输出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

1 个答案:

答案 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,并且您希望将其提取到BLBH)和0x03BL),然后例如此代码将计算:

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位,你可以写cxmov 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实模式机器代码的运行时环境。

如果不确定相关内容,只需描述您使用的所有工具和命令行/选项。