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

How to convert from BCD to ASCII and print the result?

我要输出RTC日期。

我意识到我必须从 BCD 转换为 ASCII 才能做到这一点,但我不知道该怎么做。

要转换,我使用此页面:BCD to ASCII conversion, but it uses interrupt 21h and in this page: 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

什么是BCD (binary-coded decimal)

如果BIOS时间中断结果,则使用"packed BCD"。每个字节(8 位)被分成两半 4 位(半字节),每半包含一个十进制值(4 位允许 0-15 个值,但只使用 0-9 值保留 "decimal")。

例如,如果时间以“37”秒结束,则秒字节将包含值 0x37 = 55 = 0b00110111。注意 4 位的一半在十六进制和二进制中是如何 visible/readable 的,即使是经过短暂练习后也是如此(在十进制中不是,你必须在头中计算 division/remainder 16 将它分成 3 和 7 : 55 / 16 = 3, 55 % 16 = 7).

现在汇编非常简单,有一些位摆弄,所以即使你不想深入研究像 AAM 这样的专用 BCD 指令,你也可以使用普通的位掩码 + 移位来提取这两个值。

假设您在 BL 中有值 0x37,并且您想将其提取到 BH0x03)和 BL0x07),那么例如这段代码将计算出:

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

如何显示?

这取决于您的目标平台,从问题和评论来看您的目标是在 PC 兼容计算机上使用 x86 16b 实模式,只有 BIOS 可用,因此最简单的方法之一是将这些十进制数转换0-9 值转换为数字的 ASCII 值(添加 48 有效,或者许多汇编程序会为您从字符转换,因此例如 add bl,'0' 有效,您可以将其视为 "add font character 0 to 0-9 value",并且字体中的下一个数字是从 '0' 以 +1 方式定义的,它将计算正确的字符值。

然后使用 int 10h 服务之一来显示 ASCII 字符,如 AH=0x0A 服务。显示来自 BL:

的 ASCII 字符的示例
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

因为你想显示时间,并且你有几个寄存器中的值,你可能需要在打印之间保留它们,例如,如果我要使用 int 0x1A,02 服务来读取 RTC,我将使用此 "architecture" 来显示它(不编写完整的实现,只是向您展示如何使用 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

是的,那是不合法的,因为ax是16位寄存器,而cl是8位寄存器(cx的低8b部分的别名),所以你会输8位在这样的操作中。

为了表明作为程序员你确实想要丢失高8位,你可以写mov cl,alal是低8位的别名ax).

的位

相反的转换,如 mov ax,cl 将再次失败,但作为程序员,您可以再次精确定义,8 位值应如何转换为 16 位值。

例如,要进行 "unsigned" 转换,您可以使用 386+ 指令​​:

movzx ax,cl

或8086-80286计算方式:

xor ax,ax   ; set all bits of AX to zero first
mov al,cl   ; copy the CL into lower 8 bits of AX

对于 "signed" 转换,还有专门的 386+ 方式:

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

请记住,计算机是一个更复杂的计算器,没有别的,所以你应该尝试从单词任务转换为思考你在输入端有哪些数值,哪些数值代表输出端(甚至 "characters" 只是计算机中的数值),然后您只需找出数学公式即可将输入数字转换为输出数字。

如果你有bochs,注释掉还没有编译的代码,然后从一小块可以工作的代码开始,在调试器中检查它们在做什么,这样你就可以直观地看到计算是如何进行的,然后通过 2-3 条指令慢慢添加新的代码部分,并在调试器中保持 运行ning 它们以查看它们是否计算出您想要的。 (在最近的段落中过度使用单词 "calculate" 是有意的。如果您知道如何操作计算器,那么您就非常接近如何使用汇编代码,尽管您的初始源代码可能很难看,因为它需要是时候学习 "style" 和效率了 - 只需检查指令集以了解可能的计算类型,并更好地了解 AX 是 16 位寄存器的含义,而 AL是 8 位,以及数值如何以位(电流)编码)。


"tested in ubuntu"

不完全是,你 运行 只是 nasm 在 ubuntu 中生成 16 位 x86 机器代码二进制文件(交叉编译,你可以在任何其他机器上生成这样的二进制文件,它具有x86 汇编器,与你的问题没有太大关系,除了你应该指定 NASM,所以试图回答你的人会知道使用哪种 x86 汇编语法,每个汇编器都有微小的差异)。然后您确实在虚拟机 (BOCHS) 中尝试了该二进制文件。如果您尝试直接在 ubuntu 下 运行 它,它将完全失败,因为 ubuntu OS 不支持 16b 实模式的 运行time 环境机器码。

如果不确定什么是相关的,只需描述您所有的工具,并命令您使用 lines/options。