保护模式7:中断处理
在实模式下写一个中断程序是比较简单的, DOS也直接由中断给调用, 要替换一个中断也是比较简单的. 但是一到保护模式的话, 中断就比较复杂了. 要处理的事情比较多...
首先实模式下的中断和异常都是一个概念, 但是保护模式下, 异常和中断分开了. 中断一般使用中断门, 还有陷阱门, 在写Dos下的程序经常会调用一些中断, 就是int xxx那种, 但是保护模式把这个叫自陷. 还有中断里面还带任务切换, 这个搞上那就更加复杂了. 权限什么的.复杂的很..
另外还有个蛋疼的问题, 外部中断需要重新设置, 通过8259A, BIOS初始化的时候, 已经给我们初始化了8259A, IRQ0-IRQ7被设置为对应中断08h-0Fh, 这个和保护模式下向量号08h-0Fh已经被CPU占用, 所以我们需要重新设置, 主从8259A这个东西, 说实话如果不写保护模式真没搞过, 不过现在写保护模式程序, 想要开中断那么必须设置8259A, 才了解了下, 于渊那书上也说的不清楚, 对于从保护模式退到实模式的代码, 也没有说清楚, 在Bochs上跑也有问题. 倒.
这个8259A的话, 叫做可编程中断控制器, 这个蛋疼的东西还是两片组成的, 但是分成主从两片. 要设置必须向相应的端口写入特定的ICW,主8259A对应的端口地址是20h和21h, 从8259A对应的端口地址是A0H和A1H, ICW共有4个, 每一个都具有特定格式的字节, 要了解这个的具体细节就需要参考其他资料了. 这里就不细说了. 我照搬于渊写了, 要操作端口必须按照这个套路来了..
往端口20h(主片)或A0H(从片)写入ICW1
往端口21h(主片)或A1H(从片)写入ICW2
往端口21h(主片)或A1H(从片)写入ICW3
往端口21h(主片)或A1H(从片)写入ICW4
必须按照这个套路来, 顺序不能够乱. 在写ICW2的时候就涉及中断向量号的对应, 就是这里了, 换成自己想替换的就OK, 参考下面的代码时注意观察.
说完8259A的话, 要写一个简单的搞搞中断的话也就不是太麻烦了. 看代码吧, 有图有真相
这个代码的话, 演示了一个自定义中断80h, 然后演示了时钟中断, 在时钟中断处理完毕的时候记得发送EOI给8259A, 我刚开始就忘记了这句, 然后怎么也不能够连续的处理时钟中断, 具体来说这句的意思就是告诉8259A中断我们已经处理完了.
在时钟中断中还调用了10h号中断, 看看中断嵌中断的效果.. 具体看代码的相关演示..
http://www.joenchen.com/JoenTools/Protect5.rar
;============================================================================ ;保护模式下使用中断IDT ;编译选项请参见 makefile TAB = 8 ;============================================================================ .686p Include pm.inc option casemap:none Stack_Len = 1024 ;堆栈大小 ;---------------------------------------------------------------------------- GdtSeg Segment use32 ;全局描述符 GDT label byte Dummy: Descriptor 0, 0, 0 ; 空描述符 Normal: Descriptor 0, 0ffffh, DA_DRW ; Normal 描述符 g_VideoDesc: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址 g_Code16Desc: Descriptor 0, 0ffffh, DA_C ;非一致代码段16位 g_DataDesc: Descriptor 0, DataLen-1, DA_DRWA ;数据段 g_StackDesc: Descriptor 0, Stack_Len-1, DA_DRWA ;堆栈段 g_IdtCode32Desc: Descriptor 0,IdtCode32SegLen-1, DA_CR or DA_32 ; 非一致代码段, 32 ;---------------------------------------------------------------------------- NormalSelector equ Normal - GDT ;规范段选择子 Code16Selector equ g_Code16Desc - GDT ;代码段选择子 g_DataSelector equ g_DataDesc - GDT ;数据段 g_VideoSelector equ g_VideoDesc - GDT ;视频段选择子 g_StackSelector equ g_StackDesc - GDT ;堆栈段 g_IdtCodeSelector equ g_IdtCode32Desc - GDT ;中断代码段 ;---------------------------------------------------------------------------- GDTLen equ $ - GDT ;GDT长度 _GDT_Ptr word GDTLen-1 ;VGDT dword 0 _IDT_Ptr word 0 ;VIDT dword 0 _RegSp word ? ;用于保存SS:SP _RegSs word ? GdtSeg Ends ;============================================================================ StackSeg Segment use32 ;堆栈段 byte Stack_Len dup (0) StackSeg Ends ;============================================================================ DataSeg Segment use32 ;数据段 SzMessage byte "Other Interrupt, 0h-19h 21h - 7fh", 0 SzInterrupt byte "Clock Interrupt 20h!", 0 SzUser byte "User Interrupt! 80h", 0 _byImreg byte 0 ;中断屏蔽寄存器(IMREG)值 _dwFlags dword 0 ;标记 DataLen equ $ - DataSeg ;数据段长度 DataSeg Ends ;============================================================================ IdtCode32Seg Segment use32 ;IDT中断代码段 SpuriousHandlerEntry equ $ - IdtCode32Seg;其他类型的中断 SpuriousHandler Proc mov ah, 0Ch ; 0000: 黑底 1100: 红字 mov esi, offset SzMessage mov edi, (80 * 7 + 5) * 2 ; 屏幕第 7 行, 第 5 列。 mov ecx, sizeof SzMessage ;显示的字符串 @@: lodsb stosw loop @b iretd SpuriousHandler Endp ;---------------------------------------------------------------------------- ClockHandlerEntry equ $ - IdtCode32Seg;时钟中断 ClockHandler Proc mov ah, 0Ch ; 0000: 黑底 1100: 红字 mov esi, offset SzInterrupt mov edi, (80 * 6 + 5) * 2 ; 屏幕第 6 行, 第 5 列。 mov ecx, sizeof SzInterrupt ;显示的字符串 dec dword ptr ds:[_dwFlags] @@: lodsb stosw loop @b mov eax, dword ptr ds:[_dwFlags] ;显示随机数 stosw shr eax, 16 stosw int 10h ;调用自定义中断 mov al, 20h out 20h, al ;发送EOI, 通知中断控制器中断处理完毕 iretd ClockHandler Endp ;---------------------------------------------------------------------------- UserIntHandlerEntry equ $ - IdtCode32Seg;80H用户自己的中断 UserIntHandler Proc mov ah, 0Ch ; 0000: 黑底 1100: 红字 mov esi, offset SzUser mov edi, (80 * 5 + 5) * 2 ; 屏幕第 5 行, 第 5 列。 mov ecx, sizeof SzUser ;显示的字符串 @@: lodsb stosw loop @b iretd UserIntHandler Endp ;---------------------------------------------------------------------------- IdtCode32SegLen equ $ - IdtCode32Seg ;段长度 IdtCode32Seg Ends ;============================================================================ IdtSeg Segment use32 ;中断描述符 IDT label byte ; 门 目标选择子, 偏移, DCount, 属性 repeat 32 Gate g_IdtCodeSelector, SpuriousHandlerEntry, 0, DA_386IGate endm ;时钟中断20h Gate g_IdtCodeSelector, ClockHandlerEntry, 0, DA_386IGate repeat 95 Gate g_IdtCodeSelector, SpuriousHandlerEntry, 0, DA_386IGate endm ;自定义中断80h Gate g_IdtCodeSelector, UserIntHandlerEntry, 0, DA_386IGate IDTLen equ $ - IDT ;段长度 IdtSeg Ends ;============================================================================ Code16Seg Segment use16 _Delay Proc ;延时过程 nop nop nop nop ret _Delay Endp ;---------------------------------------------------------------------------- ;设置8259A中断控制器 从20h-->28h ;---------------------------------------------------------------------------- _SetProcteMode8259A Proc uses es in al, 21h mov byte ptr ds:[_byImreg], al ; 保存中断屏蔽寄存器(IMREG)值 mov al, 011h out 020h, al ; 主8259, ICW1. call _Delay out 0A0h, al ; 从8259, ICW1. call _Delay mov al, 020h ; IRQ0 对应中断向量 0x20 out 021h, al ; 主8259, ICW2. call _Delay mov al, 028h ; IRQ8 对应中断向量 0x28 out 0A1h, al ; 从8259, ICW2. call _Delay mov al, 004h ; IR2 对应从8259 out 021h, al ; 主8259, ICW3. call _Delay mov al, 002h ; 对应主8259的 IR2 out 0A1h, al ; 从8259, ICW3. call _Delay mov al, 001h out 021h, al ; 主8259, ICW4. call _Delay out 0A1h, al ; 从8259, ICW4. call _Delay ;---------------------------------------------------------------------------- ;需要开启某个中断只需要将某一位复位即可 ;---------------------------------------------------------------------------- ;mov al, 11111111b ; 屏蔽主8259所有中断 mov al, 11111110b ; 仅仅开启定时器中断 out 021h, al ; 主8259, OCW1. call _Delay mov al, 11111111b ; 屏蔽从8259所有中断 out 0A1h, al ; 从8259, OCW1. call _Delay ret _SetProcteMode8259A Endp ;---------------------------------------------------------------------------- ;设置8259A符合实模式要求, 这个有点问题, 再Bochs里面跑不了 ;---------------------------------------------------------------------------- _SetRealmode8259A Proc mov al, 017h out 020h, al ; 主8259, ICW1. call _Delay mov al, 008h ; IRQ0 对应中断向量 0x8 out 021h, al ; 主8259, ICW2. call _Delay mov al, 001h out 021h, al ; 主8259, ICW4. call _Delay mov al, byte ptr ds:[_byImreg] ;恢复中断屏蔽寄存器(IMREG)的原值 out 021h, al call _Delay ret _SetRealmode8259A Endp ;---------------------------------------------------------------------------- _GoToProtect Proc ;返回实模式 xchg bx, bx call _SetRealmode8259A ;设置实模式中断 mov ax, NormalSelector mov fs, ax ;规范选择子 mov es, ax mov ds, ax mov ss, ax mov eax, cr0 ;关PE位, 进入实模式 and al, 0feh mov cr0, eax ;刷新段选择子缓冲区, 退回实模式 Jmp16 <seg StartCode >, < offset _RetProtect > _GoToProtect Endp ;---------------------------------------------------------------------------- _ProtectEntry Proc mov ax, g_DataSelector mov ds, ax ; 数据段选择子 mov es, ax mov ax, g_VideoSelector mov gs, ax ; 视频段选择子 mov es, ax mov ax, g_StackSelector mov ss, ax ; 堆栈段选择子 mov esp, Stack_Len mov eax, 2000 mov dword ptr ds:[_dwFlags],eax ; 初始化中断20次 call _SetProcteMode8259A ;初始化8259A中断控制器 int 080h ;没有开中断就可用调用中断门 sti @@: cmp dword ptr ds:[_dwFlags], 0 jnz @b call _GoToProtect ;返回实模式 _ProtectEntry Endp Code16Seg Ends ;============================================================================ StartCode Segment use16 _InitGdt Proc uses es ;初始化全局描述符表 local _Idt_Ptr:fword xor eax, eax mov ax, GdtSeg mov es, ax ;es-->全局描述符表 ;---------------------------------------------------------------------------- shl eax, 4 add eax, offset GDT mov dword ptr es:[_GDT_Ptr+2], eax ;初始化VGDT描述符 ;---------------------------------------------------------------------------- xor eax, eax mov ax, IdtSeg shl eax, 4 add eax, offset IDT mov word ptr ss:[_Idt_Ptr], IDTLen - 1 ;VIDT限长 mov dword ptr ss:[_Idt_Ptr+2], eax ;初始化VIDT描述符 ;---------------------------------------------------------------------------- xor eax, eax mov ax, Code16Seg ;初始化十六位的代码段 shl eax, 4 mov word ptr es:[g_Code16Desc+2], ax ;段基址低位 shr eax, 16 mov byte ptr es:[g_Code16Desc+4], al ;段基址高地址低位 mov byte ptr es:[g_Code16Desc+7], ah ;段基址高地址高位 ;---------------------------------------------------------------------------- xor eax, eax ;数据段 mov ax, DataSeg shl eax, 4 mov word ptr es:[g_DataSelector+2], ax shr eax, 16 mov byte ptr es:[g_DataSelector+4], al mov byte ptr es:[g_DataSelector+7], ah ;---------------------------------------------------------------------------- xor eax, eax ;中断代码段 mov ax, IdtCode32Seg shl eax, 4 mov word ptr es:[g_IdtCode32Desc+2], ax shr eax, 16 mov byte ptr es:[g_IdtCode32Desc+4], al mov byte ptr es:[g_IdtCode32Desc+7], ah ;---------------------------------------------------------------------------- xor eax, eax ;堆栈段 mov ax, StackSeg shl eax, 4 mov word ptr es:[g_StackDesc+2], ax shr eax, 16 mov byte ptr es:[g_StackDesc+4], al mov byte ptr es:[g_StackDesc+7], ah ;---------------------------------------------------------------------------- sidt fword ptr es:[_IDT_Ptr] ;保存IDT lgdt fword ptr es:[_GDT_Ptr] ;装载GDT lidt fword ptr ss:[_Idt_Ptr] ;装载IDT ret _InitGdt Endp ;============================================================================ Jmain Proc mov ax, GdtSeg mov ds, ax call _InitGdt ;装载GDT和IDT _EnableA20 ;关中断开A20地址线 cli mov eax, cr0 or eax, 1 mov cr0, eax ;开启分段, 进入保护模式 ;---------------------------------------------------------------------------- Jmp16 Code16Selector, <offset _ProtectEntry> ;跳入保护模式 Jmain Endp ;============================================================================ _RetProtect Proc ;返回保护模式 mov ax, GdtSeg mov ds, ax mov ax, ds:[ _RegSs ] ;恢复SS:SP mov ss, ax mov ax, ds:[ _RegSp ] mov sp, ax lidt fword ptr ds:[_IDT_Ptr] ;恢复IDT _DisableA20 ;关A20地址线, 开中断 sti mov ax, 4c00h int 21h _RetProtect Endp StartCode Ends End Jmain
发表评论
Warning: Undefined variable $user_ID in /www/wwwroot/joenchen.com/wp-content/themes/x-agan/comments.php on line 66
您必须登录 才能进行评论。