学习笔记
《x86汇编语言:从实模式到保护模式》
https://www.jianshu.com/p/d481cb547e9f
习题9-1:对8259芯片编程,屏蔽除RTC外的其他所有中断,观察字符“@”的变化速度
运行结果
- 屏蔽完除了RTC以外的所有中断后,字符@的变化速度和秒的变化同步
完整源码(根据配书代码 c09_1.asm 修改而来)
;======================================================================
;用户程序开始
;======================================================================
;代码清单9-1
;文件名:code_9-1.asm
;文件说明:用户程序
;代码功能:显示动态时钟
;创建日期:7:20 2018/5/27
;======================================================================
;头部段
;======================================================================
SECTION header vstart=0
;用户程序长度
program_length dd program_end ;[0x00]
;用户程序入口地址
code_entry dw start ;[0x04]
dd section.code.start ;[0x06]
;段重定位表项长度
realloc_tbl_len dw (header_end - realloc_begin)/4 ;[0x0a]
;段重定位表项
realloc_begin:
code_segment dd section.code.start ;[0x0c]
data_segment dd section.data.start ;[0x14]
stack_segment dd section.stack.start ;[0x1c]
header_end:
;======================================================================
;代码段
;======================================================================
SECTION code align=16 vstart=0
;-------------------------------------------------------------------------------
;0x70号中断程序
;-------------------------------------------------------------------------------
new_int_0x70: ;新的0x70中断
;在屏幕上显示 时分秒
push ax
push bx
push cx
push dx
push es
;读RTC寄存器A,根据UIP位的状态来决定是等待更新周期结束还是继续往下执行
.w0:
mov al,0x0a ;阻断NMI RTC寄存器A 第7位UIP位
or al,0x80
out 0x70,al
in al,0x71
test al,0x80
jnz .w0
;更新周期结束中断
xor al,al ;al = 0
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(秒)
push ax
mov al,2
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(分)
push ax
mov al,4
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(时)
push ax
mov al,0x0c ;RTC寄存器C 开发NMI
out 0x70,al
in al,0x71 ;读一下RTC的寄存器C,否则只发生一次中断
mov ax,0xb800
mov es,ax
pop ax
call bcd_to_ascii
mov bx,12*160+36*2 ;从屏幕上的12行36列开始显示
mov [es:bx],ah
mov [es:bx+2],al ;显示两位小时数字
mov byte [es:bx+4],':'
not byte [es:bx+5]
pop ax
call bcd_to_ascii
mov [es:bx+6],ah
mov [es:bx+8],al ;显示两位分钟数字
mov byte [es:bx+10],':'
not byte [es:bx+11]
pop ax
call bcd_to_ascii
mov [es:bx+12],ah
mov [es:bx+14],al ;显示两位秒钟数字
mov al,0x20 ;中断结束命令EOI(End Of Interrupt)
out 0xa0,al ;向8259芯片从片(Slave)发送EOI
out 0x20,al ;向8259芯片主片(Master)发送EOI
pop es
pop dx
pop cx
pop bx
pop ax
iret
;-------------------------------------------------------------------------------
;子程序: bcd_to_ascii
;参数: AL = BCD码
;返回: AH 十位数的ASCII码
; AL 个位数的ASCII码
;-------------------------------------------------------------------------------
bcd_to_ascii: ;新0x70中断中调用的子程序
;将BCD码转换成ASCII
mov ah,al
and al,0x0f
add al,0x30 ;个位数的ASCII码
shr ah,4
and ah,0x0f
add ah,0x30 ;十位数的ASCII码
ret
;-------------------------------------------------------------------------------
;用户程序入口
;-------------------------------------------------------------------------------
start: ;用户程序入口
;设置寄存器
mov ax,[stack_segment]
mov ss,ax
mov sp,ss_pointer
mov ax,[data_segment]
mov ds,ax
;显示信息,调用子程序 put_string
mov bx,init_msg ;显示初始信息
call put_string
mov bx,inst_msg ;显示安装信息
call put_string
;计算0x70号中断在中断向量表(IVT)中的入口地址
mov al,0x70
mov bl,4
mul bl
mov bx,ax
;将0x70号中断的入口地址改写为 cs:new_int_0x70
cli
push es
mov ax,0x0000
mov es,ax
mov word [es:bx],new_int_0x70 ;偏移地址
mov word [es:bx+2],cs ;段地址
pop es
;0x70 [索引端口],用来指定CMOS RAM内的单元
;0x71 [数据端口],用来读写CMOS RAM相应单元里的内容
;现在要访问的就是位于CMOS RAM中的RTC(REAL TIME CLOCK)
mov al,0x0b ;RTC寄存器B
or al,0x80 ;端口0x70的最高位(bit 7)是控制NMI的开关,
out 0x70,al ; 0表示允许NMI中断到达处理器、1表示阻断所有NMI信号
mov al,0x12 ;设置“更新周期结束中断”
out 0x71,al
mov al,0x0c ;RTC寄存器C
out 0x70,al
in al,0x71 ;读一下RTC寄存器C,使之可以产生新的中断信号
;处理 从片slave
mov al,0xfe ;0xfe = 1111 1110B 清除第0位(此位通过从片引脚IRO连接着RTC)
out 0xa1,al ;回写寄存器
;处理 主片master
mov al,0xfb ;只打开引脚IR2 使从片连接上主片
out 0x21,al ;回写
sti
;显示信息,调用子程序 put_string
mov bx,done_msg
call put_string
mov bx,tips_msg
call put_string
;显示标志 @ 符号
mov cx,0xb800
mov ds,cx
mov byte [12*160+32*2],'@'
;创建循环 停机状态响应外部中断恢复执行
.idle:
hlt ;使CPU进入低功耗状态,直到用外部中断唤醒
not byte [12*160+32*2+1] ;反转显示属性
jmp .idle
;-------------------------------------------------------------------------------
;子程序: put_string
;功能: 显示字符串,字符串以0结尾
;-------------------------------------------------------------------------------
put_string: ;显示串(0结尾)。
;输入:DS:BX=串地址
mov cl,[bx]
or cl,cl ;cl=0 ?
jz .exit ;是的,返回主程序
call put_char
inc bx ;下一个字符
jmp put_string
.exit:
ret
;-------------------------------------------------------------------------------
put_char: ;显示一个字符
;输入:cl=字符ascii
push ax
push bx
push cx
push dx
push ds
push es
;以下取当前光标位置
mov dx,0x3d4
mov al,0x0e
out dx,al
mov dx,0x3d5
in al,dx ;高8位
mov ah,al
mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
in al,dx ;低8位
mov bx,ax ;BX=代表光标位置的16位数
cmp cl,0x0d ;回车符?
jnz .put_0a ;不是。看看是不是换行等字符
mov ax,bx ;此句略显多余,但去掉后还得改书,麻烦
mov bl,80
div bl
mul bl
mov bx,ax
jmp .set_cursor
.put_0a:
cmp cl,0x0a ;换行符?
jnz .put_other ;不是,那就正常显示字符
add bx,80
jmp .roll_screen
.put_other: ;正常显示字符
mov ax,0xb800
mov es,ax
shl bx,1
mov [es:bx],cl
;以下将光标位置推进一个字符
shr bx,1
add bx,1
.roll_screen:
cmp bx,2000 ;光标超出屏幕?滚屏
jl .set_cursor
mov ax,0xb800
mov ds,ax
mov es,ax
cld
mov si,0xa0
mov di,0x00
mov cx,1920
rep movsw
mov bx,3840 ;清除屏幕最底一行
mov cx,80
.cls:
mov word[es:bx],0x0720
add bx,2
loop .cls
mov bx,1920
.set_cursor:
mov dx,0x3d4
mov al,0x0e
out dx,al
mov dx,0x3d5
mov al,bh
out dx,al
mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
mov al,bl
out dx,al
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
ret
;======================================================================
;数据段
;======================================================================
SECTION data align=16 vstart=0
init_msg db 'Starting...',0x0d,0x0a,0
inst_msg db 'Installing a new interrupt 70H...',0
done_msg db 'Done.',0x0d,0x0a,0
tips_msg db 'Clock is now working.',0
;======================================================================
;栈段
;======================================================================
SECTION stack align=16 vstart=0
resb 256
ss_pointer:
;======================================================================
;尾部段
;======================================================================
SECTION program_trail
program_end:
;======================================================================
;用户程序结束
;======================================================================
代码说明
- 屏蔽完除了RTC中断外的全部中断
;处理 从片slave
mov al,0xfe ;0xfe = 1111 1110B 清除第0位(此位通过从片引脚IRO连接着RTC)
out 0xa1,al ;回写寄存器
;处理 主片master
mov al,0xfb ;只打开引脚IR2 使从片连接上主片
out 0x21,al ;回写
-
0xfe
和0xfb
是什么意思?
8259芯片主片以及从片各有一个IMR(中断屏蔽寄存器),
该寄存器是8位寄存器,寄存器的值与8个引脚一一对应(IR0~IR7),
从引脚而来的中断信号,0表示允许中断,1表示阻断中断;
0xfe = 1111 1110B ,就是打开从片上的引脚IR0
使得 RTC 与从片相连,这样RTC的中断就可以传到从片,并且从片上其余引脚全部阻断;
0xfb = 1111 1011B,就是打开主片上的引脚IR2
使得 从片与主片相连,并且主片上其余引脚全部阻断;
RTC-从片-主片-处理器
就连好了,并且除了RTC以外的中断全部被屏蔽。