本文的示例代码显示了开机之后,屏幕左上角显示时分秒的实时时间
由于显示的是动态始终,这就得先说一下CPU关于时钟芯片的一些注意项,下面的图3画出了CPU关于中断的两个引脚,NMI和INTR
NMI: 原则上NMI过来的中断源都必须被处理执行,但是这只是原则上,我们可以通过设置0x70端口的位7(最高位)为1,来屏蔽所有不可屏蔽的中断,图3中示意出了NMI与70端口通过与门相连,所以需要设置1,而不是0
INTR: 如果发生了中断,INTR会有信号过来,但是信号只是一个高电平1而已,但是具体发生了什么中断?CPU不得而知,这个时候就需要有一个中断控制器,在告知CPU有中断发生之后,CPU决定是否处理(标志寄存器IF标志位),如果需要处理,则通过总线,去中断控制器里读取中断号(回忆图2),找到中断程序所在地址,最后跳转到那里去执行代码,下面来说说安装中断程序的过程,如图4
不得不说,这个图4我画的确实不好,但是我真的没有想到更好的方式来简单的阐述8259芯片和RTC的关系,图中没有画出CMOS中ACD寄存器,也没有画出8259A主片和从片的级联关系,这是因为我觉得画多了,不好看懂,但是我画少了之后,发现效果也不是很好,只好等以后有时间再重画这个电路图了
安装中断程序步骤
1.首先设置NMI引脚为0,表示屏蔽所有不可屏蔽中断
2.设置标志寄存器的IF位为1,表示屏蔽所有可屏蔽中断
经过以上两步,CPU的执行流程就不会受中断影响了,因为如果在安装中断的过程中,发生了其他中断,很有可能程序将加载错误的地址
3.设置CMOS BIOS中的寄存器B,需要将寄存器B的一些位置1才行,分别是位7禁止每秒去bios更新时间,位4开启更新周期结束中断,位2表示使用BCD,位1开启24小时制
4.安装0x70号中断,因为RTC就是70号中断,所以要完成在屏幕上实时显示时间,就得安装70号中断,注意中断程序执行完毕之后,程序代码必须要做两件事,读取寄存器C和往8259A的两个端口写0x20命令,告知中断控制器8259芯片中断完成,否则下次中断会被8259卡在外面无法进来
5.安装完毕之后,就可以将之前的NMI和IF恢复了,然后读取一下寄存器C,因为发生中断之后,如果不读取一下寄存器C,则中断不会再次发生
cli ;关掉可屏蔽中断 mov al,0x0b ;设置索引为寄存器B,同时关闭不可屏蔽中断NMI引脚 or al,0x80 out 0x70,al mov al,0x12 ;设置寄存器B,位7禁止每秒去bios更新时间 out 0x71,al ;位4开启更新周期结束中断,位2BCD,位1开启24小时制 mov ax,0 ;开始安装70号中断 mov es,ax mov al,0x70 mov bl,4 mul bl mov si,ax mov bx,show_time mov [es:si],bx mov word [es:si+2],0x7c0 mov al,0x0c ;随便读取一下寄存器C,否则中断不会发生 out 0x70,al in al,0x71 ;设置70引脚的值是0这样才能接受RTC的中断信号 in al,0xa1 ;读8259从片的IMR寄存器 and al,0xfe ;设置最低位为0,其他位不变,所以and一下 out 0xa1,al ;写回从片IMR寄存器 sti ;打开可屏蔽中断 dont_stop: ;hlt低功耗运行,等待各种中断的发生 hlt jmp dont_stop show_time: mov ax,0xb800 mov ds,ax mov al,0x04 ;从04索引读小时BCD信息保存到al out 0x70,al in al,0x71 call show_BCD_number mov byte [0],al mov byte [1],0x0b mov byte [2],bl mov byte [3],0x0b mov byte [4],':' mov byte [5],0x0b mov al,0x02 ;从02索引读分钟BCD信息保存到al out 0x70,al in al,0x71 call show_BCD_number mov byte [6],al mov byte [7],0x0b mov byte [8],bl mov byte [9],0x0b mov byte [10],':' mov byte [11],0x0b mov al,0x00 ;从00索引读分钟BCD信息保存到al out 0x70,al in al,0x71 call show_BCD_number mov byte [12],al mov byte [13],0x0b mov byte [14],bl mov byte [15],0x0b mov al,0x20 ;设置摩托罗拉8259A芯片的值是HEX20表示中断已经结束 out 0xa0,al out 0x20,al mov al,0x0c out 0x70,al in al,0x71 iret ;处理BCD编码,将al中的BCD码转换成ascii ;将al高4放到al低4位中,并且al低4位保存的是ascii ;将al低4为放到bl低4中,并且bl低4位保存的是ascii show_BCD_number: xor bx,bx mov bl,al shr al,4 ;保留高4位 add al,48d ;BCD转换ASICC需要+64 and bl,0x0F add bl,48d ret times 510-($-$$) db 0 dw 0xAA55