16位实模式OS中RTC读取值异常问题求助
解决RTC读取返回异常时间值的问题
嘿,这个坑我当年折腾实模式OS的时候实打实踩过!你看到的“每分钟90秒、每小时90分钟”本质是RTC默认输出BCD编码,而你直接把它当成十进制数打印了。
问题根源
RTC(实时时钟)的时间字段(秒、分、时等)默认是以BCD(Binary-Coded Decimal)格式存储的。比如59秒在BCD里是0x59,如果你直接把这个十六进制值转成十进制打印,就会得到89(因为0x59的十进制就是89)——这就是你看到0-89秒的原因,分钟和小时的问题也是同理。
修复方案:BCD转十进制
你需要在读取端口值后,把BCD编码转换成真正的十进制数,再传给print_decimal。修改你的代码片段如下:
get_seconds: xor ax, ax ; 清空AX寄存器 mov al, 0x00 ; 指定读取秒数的索引 out 0x70, al ; 向RTC索引端口写入索引值 in al, 0x71 ; 读取RTC数据端口的秒数(此时是BCD格式) ; 核心:BCD转十进制 mov ah, al ; 把BCD值暂存到AH shr ah, 4 ; 右移4位,取出高4位(十位数字) and al, 0x0f ; 保留低4位,取出个位数字 mov cl, 10 mul cl ; 十位数字乘以10(结果存在AL) add al, ah ; 加上个位数字,此时AL就是十进制的秒数 call print_decimal ; 打印十进制值
可选优化:切换RTC到二进制模式
如果你不想每次读取都做BCD转换,可以通过修改RTC的控制寄存器B,让它直接输出二进制格式的数据:
; 设置RTC为二进制输出模式(只需执行一次) setup_rtc_binary: mov al, 0x0B ; 指定控制寄存器B的索引 out 0x70, al in al, 0x71 ; 读取当前寄存器B的值 or al, 0x04 ; 置位第2位(DM位),启用二进制输出 out 0x71, al ; 写回修改后的值 ret
之后读取RTC的所有时间字段都会是二进制数,直接调用print_decimal就能得到正确的0-59秒、0-59分这类结果。
额外提醒:避免读取到更新中的数据
读取RTC时最好先等待RTC完成当前的更新操作,不然可能读到半更新的错误值。可以在读取前加一段等待逻辑:
; 等待RTC更新完成,确保读取的数据稳定 wait_rtc_update: mov al, 0x0A ; 指定控制寄存器A的索引 out 0x70, al in al, 0x71 test al, 0x80 ; 检查UIP位(第7位),1表示正在更新 jnz wait_rtc_update ; 如果正在更新,继续等待
把这段代码放在get_seconds的开头,就能避免因RTC正在更新导致的随机错误。
内容的提问来源于stack exchange,提问作者bjarke15
相关产品推荐
相关产品推荐

