首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > 移动开发 >

保护模式准备知识-书中pmtest9中断机制代码详细注释

2013-03-13 
保护模式预备知识--书中pmtest9中断机制代码详细注释 一、80386,内存,8255A的连接如图1图 1二、编程8255A中

保护模式预备知识--书中pmtest9中断机制代码详细注释

 一、80386,内存,8255A的连接如图1

            保护模式准备知识-书中pmtest9中断机制代码详细注释

                                                                            图 1               


二、编程8255A中断控制器(将ICW写入特定的寄存器)

       8255A是可编程中断控制器,对它的设置并不复杂,是通过向相对应的端口写入特定的ICW(Initialization Command Word)来实现的。主8259A对应的端口地址是20h和21h,从8259A对应的端口地址是A0h和A1h。ICW共有4个,每一个都是具有特定格式的字节,为了先对初始化8259A的过程有一个概括的了解,我们过一会再来关注每一个ICW的格式,现在,先来看一下初始化过程:

       1、往端口20h(主片)或A0h(从片)写入ICW1。

       2、往端口21h(主片)或A1h(从片)写入ICW2。

       3、往端口21h(主片)或A1h(从片)写入ICW3。

       4、往端口21h(主片)或A1h(从片)写入ICW4。


       ICW格式如图2:

       保护模式准备知识-书中pmtest9中断机制代码详细注释

       保护模式准备知识-书中pmtest9中断机制代码详细注释

       保护模式准备知识-书中pmtest9中断机制代码详细注释  保护模式准备知识-书中pmtest9中断机制代码详细注释

                                                       图 2 ICW的格式                                  图 3 OCW1

       若想屏蔽或者打开外部中断,只需要往8259A写入OCW1就可以了,即写入中断屏蔽寄存器(IMR),OCW1的格式如图3。

       可屏蔽中断与NMI的区别在于是否受到IF位的影响,而8259A的中断屏蔽寄存器(IMR)也影响着中断是否会被响应。所以,外部可屏蔽中断的发生就会受到两个因素的影响,只有当IF位为1,并且IMR相应位为0时才会发生。除单步中断外,所有内部中断都无法禁止,即不能通过CLI指令使IF=0,使其不响应。


三、保护模式下的中断

       1、0-19的中断和异常已经安排好了,如书上89页表3.8


       2、有特权级变化时,中断堆栈变化如图4。从中断或异常返回时必须使用指令iretd,它和ret很相似,只是它同时会改变eflags的值。需要注意的是,只有当CPL为0时,eflags中的IOPL域才会改变,而且只有当CPL小于等于IOPL时,IF才会被改变。另外,iretd执行时Error Code不会被自动从堆栈弹出,所以执行它之前要先将它从栈中清除掉。指令in、ins、out、outs、cli、sti只有在CPL小于等于IOPL时才能执行。这些指令被称为I/O敏感指令。如果低特权级的指令试图访问这些I/O敏感指令将会导致常规保护错误(#GP)。

       保护模式准备知识-书中pmtest9中断机制代码详细注释

                                                       图 4 中断或异常发生时的堆栈


       3、I/O许可位

       TSS偏移102字节处有一个被称做"I/O许可位图基址"的东西,它是一个以TSS的地址为首地址的偏移,指向的便是I/O许可位图。之所以叫做位图,是因为它的每一位表示一个字节的端口地址是否可用。如果某一位为0,则表示此位对应的端口号可用,为1则不可用。由于每一个任务都可以有单独的TSS,所以每个任务可以有它单独的I/O许可位图。


       4、保护模式下中断执行过程

      以下由硬件自动完成:

      ①从中断信息中取得中断类型码。

      ②标志寄存器eflags的值入栈,因为在中断过程中要改变标志寄存器的值,所以先将其保存在栈中。

      ③设置标志寄存器IF和TF位为0,为了防止在此中断中再来其他外部中断。

      ④cs,eip,error code入栈。

      ⑤根据中断类型码在IDT中找到中断门执行代码。

      IRETD 指令先弹出一个32位的EIP值,然后再弹出一个32位值并将最低的2个字节值传入CS寄存器,最后再弹出一个32位的标志寄存器值。


      pmtest9.asm代码如下:

%include"pm.inc"; 常量, 宏, 以及一些说明org07c00hjmpLABEL_BEGIN[SECTION .gdt]; GDT;                              段基址,       段界限     , 属性LABEL_GDT:   Descriptor       0,                0, 0           ; 空描述符LABEL_DESC_NORMAL:Descriptor       0,            0ffffh, DA_DRW; Normal 描述符LABEL_DESC_CODE32: Descriptor       0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段LABEL_DESC_CODE16:Descriptor       0,            0ffffh, DA_C; 非一致代码段, 16LABEL_DESC_VIDEO:  Descriptor 0B8000h,           0ffffh, DA_DRW     ; 显存首地址; GDT 结束GdtLenequ$ - LABEL_GDT; GDT长度GdtPtrdwGdtLen - 1; GDT界限dd0; GDT基地址; GDT 选择子SelectorNormalequLABEL_DESC_NORMAL- LABEL_GDTSelectorCode32equLABEL_DESC_CODE32- LABEL_GDTSelectorCode16equLABEL_DESC_CODE16- LABEL_GDTSelectorVideoequLABEL_DESC_VIDEO- LABEL_GDT; END of [SECTION .gdt][SECTION .data1] ; 数据段ALIGN32[BITS32]LABEL_DATA:; 实模式下使用这些符号; 变量_wSPValueInRealModedw0_SavedIDTR:dd0; 用于保存 IDTRdd0_SavedIMREG:db0; 中断屏蔽寄存器值DataLenequ$ - LABEL_DATA; END of [SECTION .data1]; IDT[SECTION .idt]ALIGN32[BITS32]LABEL_IDT:;最多256个中断门,每个占8个字节,所以IDT最大占2K。前面20个已被占用。; 门                目标选择子,            偏移, DCount, 属性%rep 32               GateSelectorCode32, SpuriousHandler,      0, DA_386IGate%endrep.020h:GateSelectorCode32,    ClockHandler,      0, DA_386IGate   ;转换成10进制是32%rep 95GateSelectorCode32, SpuriousHandler,      0, DA_386IGate%endrep.080h:GateSelectorCode32,  UserIntHandler,      0, DA_386IGate ;转换为10进制是128IdtLenequ$ - LABEL_IDTIdtPtrdwIdtLen - 1; 段界限dd0; 基地址; END of [SECTION .idt][SECTION .s16][BITS16]LABEL_BEGIN:movax, csmovds, axmoves, axmovss, axmovsp, 0100hmov[LABEL_GO_BACK_TO_REAL+3], axmov[_wSPValueInRealMode], sp; 初始化 16 位代码段描述符movax, csmovzxeax, axshleax, 4addeax, LABEL_SEG_CODE16movword [LABEL_DESC_CODE16 + 2], ax ;ds:LABEL_DESC_CODE16shreax, 16movbyte [LABEL_DESC_CODE16 + 4], almovbyte [LABEL_DESC_CODE16 + 7], ah; 初始化 32 位代码段描述符xoreax, eaxmovax, csshleax, 4addeax, LABEL_SEG_CODE32movword [LABEL_DESC_CODE32 + 2], axshreax, 16movbyte [LABEL_DESC_CODE32 + 4], almovbyte [LABEL_DESC_CODE32 + 7], ah; 为加载 GDTR 作准备xoreax, eaxmovax, dsshleax, 4addeax, LABEL_GDT; eax <- gdt 基地址movdword [GdtPtr + 2], eax; [GdtPtr + 2] <- gdt 基地址; 为加载 IDTR 作准备xoreax, eaxmovax, dsshleax, 4addeax, LABEL_IDT; eax <- idt 基地址movdword [IdtPtr + 2], eax; [IdtPtr + 2] <- idt 基地址; 保存 IDTRsidt[_SavedIDTR] ;保存IDTR寄存器的值到ds:_SavedIDTR; 保存中断屏蔽寄存器(IMREG)值inal, 21hmov[_SavedIMREG], al  ; 加载 GDTRlgdt[GdtPtr]; 关中断cli ;int 80h可以起作用,但是可屏蔽中断(通过8255设置的)就不能响应了 IF=0; 加载 IDTRlidt[IdtPtr]; 打开地址线A20inal, 92horal, 00000010bout92h, al; 准备切换到保护模式moveax, cr0oreax, 1movcr0, eax; 真正进入保护模式jmpdword SelectorCode32:0; 执行这一句会把 SelectorCode32 装入 cs,; 并跳转到 Code32Selector:0  处LABEL_REAL_ENTRY:; 从保护模式跳回到实模式就到了这里movax, csmovds, axmoves, axmovss, axmovsp, [_wSPValueInRealMode]lidt[_SavedIDTR]; 恢复 IDTR 的原值moval, [_SavedIMREG]; ┓恢复中断屏蔽寄存器(IMREG)的原值out21h, al; ┛inal, 92h; ┓andal, 11111101b; ┣ 关闭 A20 地址线out92h, al; ┛sti; 开中断movax, 4c00h; ┓int21h; ┛回到 DOS; END of [SECTION .s16][SECTION .s32]; 32 位代码段. 由实模式跳入.[BITS32]LABEL_SEG_CODE32:movax, SelectorVideomovgs, ax; 视频段选择子(目的)callInit8259Aint080h ;内中断,类似调用门sti ;开中断后,时钟中断开始生效 IF=1jmp$ ;为了演示结果,去掉后跳回实模式callSetRealmode8259A; 到此停止jmpSelectorCode16:0; Init8259A ---------------------------------------------------------Init8259A:moval, 011hn ;00010001out020h, al; 主8259, ICW1.callio_delayout0A0h, al; 从8259, ICW1.callio_delaymoval, 020h; IRQ0 对应中断向量 0x20 00100000   因为前0-19号中断已经被占了out021h, al; 主8259, ICW2.callio_delaymoval, 028h; IRQ8 对应中断向量 0x28 00101000out0A1h, al; 从8259, ICW2.callio_delaymoval, 004h; IR2 对应从8259 00000100out021h, al; 主8259, ICW3.callio_delaymoval, 002h; 对应主8259的 IR2 0000 0010out0A1h, al; 从8259, ICW3.callio_delaymoval, 001h 0000 0001out021h, al; 主8259, ICW4.callio_delayout0A1h, al; 从8259, ICW4.callio_delay;moval, 11111111b; 屏蔽主8259所有中断moval, 11111110b; 仅仅开启定时器中断,外部可屏蔽中断只有在IF=1和IMR对应位为0时才响应out021h, al; 主8259, OCW1.callio_delaymoval, 11111111b; 屏蔽从8259所有中断out0A1h, al; 从8259, OCW1.callio_delayret; Init8259A ---------------------------------------------------------; SetRealmode8259A ---------------------------------------------------------SetRealmode8259A:moval, 017h 0001 0111out020h, al; 主8259, ICW1.callio_delay ;4个字节中断向量,单个8259moval, 008h; IRQ0 对应中断向量 0x8out021h, al; 主8259, ICW2.callio_delay ;ICW3已经不需要了moval, 001hout021h, al; 主8259, ICW4.callio_delayret; SetRealmode8259A ---------------------------------------------------------io_delay:nopnopnopnopret; int handler ---------------------------_ClockHandler:ClockHandlerequ_ClockHandler - $$incbyte [gs:((80 * 0 + 70) * 2)]; 屏幕第 0 行, 第 70 列。moval, 20hout20h, al; 发送 EOIiretd  ;IRETD 指令先弹出一个32位的EIP值,然后再弹出一个32位值并将最低的2个字节值传入CS寄存器,       ;最后再弹出一个32位的标志寄存器值_UserIntHandler:UserIntHandlerequ_UserIntHandler - $$movah, 0Ch; 0000: 黑底    1100: 红字moval, 'I'mov[gs:((80 * 0 + 70) * 2)], ax; 屏幕第 0 行, 第 70 列。iretd_SpuriousHandler:SpuriousHandlerequ_SpuriousHandler - $$movah, 0Ch; 0000: 黑底    1100: 红字moval, '!'mov[gs:((80 * 0 + 75) * 2)], ax; 屏幕第 0 行, 第 75 列。jmp$iretd; ---------------------------------------SegCode32Lenequ$ - LABEL_SEG_CODE32; END of [SECTION .s32]; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式[SECTION .s16code]ALIGN32[BITS16]LABEL_SEG_CODE16:; 跳回实模式:movax, SelectorNormalmovds, axmoves, axmovfs, axmovgs, axmovss, axmoveax, cr0andal, 11111110bmovcr0, eaxLABEL_GO_BACK_TO_REAL:jmp0:LABEL_REAL_ENTRY; 段地址会在程序开始处被设置成正确的值Code16Lenequ$ - LABEL_SEG_CODE16; END of [SECTION .s16code]

热点排行