从实模式到保护模式笔记5中断和动态时间显示
从实模式到保护模式笔记5中断和动态时间显示
0x00 代码
使用BIOS中断例程
;========================================================
SECTION header align vstart=0
program_length dd program_end
code_entry dw start
dd section.code.start
realloc_tbl_len dw (header_end-realloc_begin)/4
realloc_begin:
code_segment dd section.code.start
data_segment dd section.data.start
stack_segment dd section.stack.start
header_end:
;==========================================
SECTION code align=16 vstart=0
start:
mov ax,[stack_segment]
mov ss,ax
mov sp,ss_pointer
mov ax,[data_segment]
mov ds,ax
mov cx,msg_end-message
mov bx,message
.putc:
mov ah,0x0e
mov al,[bx]
int 0x10
inc bx
loop .putc
.reps:
mov ah,0x00
int 0x16;读键盘缓冲区
mov ah,0x0e
mov bl,0x07
int 0x10;显示
jmp .reps
;==============================================
SECTION data align=16 vstart=0
message db 'Hello,friend!',0x0d,0x0a
db 'This simple procedure used to demonstrate'
db 'the BIOS interrupt.',0x0d,0x0a;换行回车
db 'Please press the keys on the keyboard->'
msg_end:
;=============================================
SECTION program_trail
program_end:
自己写中断安装中断例程
;=================================================
SECTION header align=16 vstart=0
program_length dd program_end
code_entry dw start
dd section.code.start
realloc_tbl_len dw (header_end-realloc_begin)/4
realloc_begin:
code_segment dd section.code.start
data_segment dd section.data.start
stack_segment dd section.stack.start
header_end:
;================================================
SECTION code align=16 vstart=0
new_int_0x70:
push ax
push bx
push cx
push dx
push es
.w0:
mov al,0x0a;阻断NMI
or al,0x80
out 0x70,al
in al,0x71
test al,0x80
jnz .w0
xor al,al
or al,0x80
out 0x70,al
in al,0x71;读RTC 秒
push ax
mov al,2
or al,0x80
out 0x70,al
in al,0x71;分
push ax
mov al,4
or al,0x80
out 0x70,al
in al,0x71;时
push ax
mov al,0x0c
out 0x70,al
in al,0x71
mov ax,0xb800
mov es,ax
pop ax
call bcd_to_ascii
mov bx,12*160+36*2
mov [es:bx],ah
mov [es:bx+2],al
mov al,':'
mov [es:bx+4],al
not byte [es:bx+5];反转显示属性
pop ax
call bcd_to_ascii
mov [es:bx+6],ah
mov [es:bx+8],al
mov al,':'
mov [es:bx+10],al
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
out 0xa0,al;向从片发送命令
out 0x20,al;向主片发送命令
pop es
pop dx
pop cx
pop bx
pop ax
iret
;-----------------------------------------------
bcd_to_ascii:
mov ah,al
and al,0x0f;仅保留后四位
add al,0x30;转换成ascii
shr ah,4
and ah,0x0f;实际没必要这一步
add ah,0x30
ret
;---------------------------------------------
start:
mov ax,[stack_segment]
mov ss,ax
mov sp,ss_pointer
mov ax,[data_segment]
mov ds,ax
mov bx,init_msg
call put_string
mov bx,inst_msg
call put_string
mov al,0x70
mov bl,4
mul bl
mov bx,ax;计算0x70号中断在IVT(interrupt vertext table)的偏移
cli
push es
mov ax,0x0000
mov es,ax
mov word [es:bx],new_int_0x70
mov word [es:bx+2],cs;和当前代码一个段
pop es
mov al,0x0b;RTC寄存器B
or al,0x80;阻断NMI
out 0x70,al
mov al,0x12
out 0x71,al
mov al,0x0c
out 0x70,al
in al,0x71;读RTC寄存器c,复位未决的中断状态
in al,0xa1
and al,0xfe
out 0xa1,al
sti;重新开放中断
mov bx,done_msg
call put_string
mov bx,tips_msg
call put_string
mov cx,0xb800
mov ds,cx
mov byte [12*160+33*2],'@'
.idle:
hlt;使cpu进入低功耗状态,直到用中断唤醒
not byte [12*160+33*2+1];反转显示属性
jmp .idle
;---------------------------------------------------
put_string:;ds:bx=串地址
mov cl,[bx]
or cl,cl;cl=0?
jz .exit
call put_char
inc bx
jmp put_string
.exit:
ret
;-------------------------------------------------
put_char:
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
mov ah,al;光标位置高8位
mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
in al,dx
mov bx,ax;bx 光标位置16位二级二进制数
cmp cl,0x0d ;回车符?
jnz .put_0a;看是不是换行符
mov ax,bx
mov bl,80
div bl
mul bl
mov bx,ax;改写bx 到行开头的位置的16位二进制数
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;显示一个字符后光标前进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 inerrupt 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:
上述代码运行正确,就不放截图了
0x01 代码讲解
本章主要内容为中断,相比上一节而言更偏重硬件知识的讲解。
1.外部中断
外部中断即处理器外面来的中断信号,当处理器外部设备发生错误时,或者有数据要传送,就会通知处理去来处理。
外部硬件中断信号是通过处理器上的NMI和INTR引脚传入的NMI即not maskable interrupt 不可屏蔽中断,INTR即interrupter,这个引脚上的中断信号可以屏蔽。
也就是分为可屏蔽中断和不可屏蔽中断,可屏蔽中断一般处理比较紧急必须要处理的事件,中断号/码为2,不再细分,而可屏蔽中断则根据具体的类型有不同的终端码。
不管是NMI还是INTR都是一个引脚接受多个类型的中断源,所以,他们都需要一个代理芯片(中断控制器),该代理芯片接受多个中断源并仲裁,决定哪一个优先向处理器提出服务请求
常见的中断控制器是8259芯片,x86系列处理器允许256个中断,8259负责其中15个,但是其负责的15个的中断号并不固定 ,允许软件设置中断号防止冲突,8259可以用in和out 来改变他的状态。
由于8259只有8个输入引脚,所以是两个串连实现的15个输入引脚,直接与处理器相连的称为主片,与主片相连的是从片。可以看到RTC实时时钟与从片的IR0相连,说明RTC实时时钟可以引起外部可屏蔽中断。
在8259内部有中断屏蔽寄存器(Interrupt Mask Register ,IMR),可以屏蔽8个引脚任意部分。如果对应引脚没有被IMR屏蔽,就将送入INTR引脚,但是x86处理器内部有IF(interrupt flag)标志位,如果IF为0 则屏蔽INTR引脚 为1则不屏蔽,可以用cli 置IF为0 sti 置1
中断处理,即是一段与该中断有关的程序,根据中断码,寻找对应的中断处理程序。0-255号中断的处理程序的入口地址被放在0x00000-0x003ff 这1024字节的空间里,每个中断号的入口地址占4字节,低2字节是入口地址的偏移地址,高2字节是段地址,这个中断处理程序的入口地址表,称为中断向量表(Interrupt Vertext Table,IVT)
处理器拿到中断码后,自动完成以下动作:
①标志寄存器flag入栈
②IF和TF置0 interrupt flag 和 Trace flag(单步调试标志位)
③push cs push ip(保存当前cs ip 位置)
④跳到对应的处理程序入口
test 指令 test a,b 类似于and a,b都是按位与,但test不保存与的结果,配合有条件的跳转,用来判断数据的特征。
其余部分都是讲解RTC和CMOS 特定的硬件知识过多琐碎,不值得记录。
2.内部中断
和外部的硬件中断不同,内部中断由处理器执行的指令引起。例如,处理器遇到非法指令时,将产生中断6,意味着操作码不是一条合法的指令。
软中断是由int指令引起的中断处理,中断号直接由指令给出 如 int 16
int3不是int 3 int3是断点中断指令,在需要断下的指令前写int3 可以引发3号中断处理程序
into 是溢出中断指令,当处理器执行到这条指令时,如果OF标志位是1,那么将产生4号中断
可以为所有的终端类型自定义中断处理程序,包括内部中断、硬件中断和软中断。BIOS程序执行期间,会修改中断向量表,提供对应的中断处理程序,供操作系统调用。
实模式的讲解到此结束,开始保护模式的章节