实验9 保护模式编程基础

来源:互联网 发布:软件质量模型 编辑:程序博客网 时间:2024/05/21 06:46

实验9  保护模式编程基础

在保护模式下,可寻址高达4GB(甚至更多)的物理地址空间;支持存储器分段管理机制和分页管理机制;支持多任务;支持4个特权级和配套的特权检查机制,区分不同级别的代码。操作系统(如WindowsLinux等)正是依赖于这些特性来实现虚拟内存、内核/用户模式、多任务等功能。

9.1 虚拟机开发环境

实模式与保护模式的切换,以及保护模式下的中断、DMA处理等程序必须在特权级0下执行,而Windows应用程序在特权级3环境中运行,只有系统内核和设备驱动程序才能运行在特权级0。因此,这些实验需要在DOS环境下进行。

借助于虚拟机技术,可以在Windows环境下完成这些实验。图9-1就是在虚拟机中运行DOS操作系统和real2pro.exe的结果。

9-1 在虚拟机中完成保护模式实验

1. 虚拟机

虚拟机软件可在一台计算机(称为宿主机)上模拟出若干台计算机,模拟出的计算机(称为客户机)都有自己单独的硬件配置,可以安装单独的操作系统。例如,宿主计算机运行Windows操作系统,在上面运行虚拟机软件,虚拟出的计算机可以运行Linux操作系统。这2个操作系统各自独立运行,互不干扰。

VirtualBox是德国InnoTek公司开发的虚拟机软件,可以运行在WindowsLinux上,客户机上可以安装WindowsDOSLinuxOpenBSD等操作系统。除了VirtualBox软件以外,常用的虚拟机软件还有VMWareVirtual PC等。

2. 建立虚拟软驱

在保护模式实验中,宿主机运行Windows,客户机运行DOSDOSWindows之间的共享采用虚拟软驱来完成。

VFD是一个虚拟软驱软件。运行vfdwin.exe(在C:/asm/tools/vfd21目录下),如图9-2所示,按以下步骤在计算机上虚拟出一个软驱(B:):

(1)      在“Driver”页中,按“Browse…”选择C:/asm/tools/vfd21/vfd.sys

(2)      按“Install”按钮安装驱动程序;

(3)      按“Start 按钮启动驱动程序;

(4)      在“Drive 0”页中,按“Change…”选择B:

(5)      按“Open”按钮,再按“Browse…”选择C:/asm/tools/vfd21/ floppyA.imgDisk Type选择为FILE

 

9-2 建立虚拟软驱

这样,计算机上就增加了一个虚拟软驱B:了。

3. 创建DOS虚拟机

安装VirtualBox软件,启动VirtualBox后,点击工具栏上的“新建”按钮。按以下步骤在计算机上创建一个虚拟机:

(1)      如图9-3所示,虚拟机名称为“DOS”,在系统类型种选择“DOS”,按“下一步”;

(2)      提示系统内存容量,缺省为32MB,直接按“下一步”;

(3)      9-4中,提示选择虚拟硬盘时,按“现有”按钮,出现图9-5所示对话框;

(4)      在虚拟硬盘管理器中,按“注册”按钮,指定虚拟硬盘映像文件为C:/asm/tools/vbox/ HDD0.VDI,如图9-6所示。在图9-5的虚拟硬盘管理器,按“选择”按钮;

(5)      一直按“下一步” 按钮,直到“完成”为止。

 

9-3 设置虚拟机名称

9-4 选择虚拟硬盘

9-5 虚拟硬盘管理器

9-6 指定虚拟硬盘映像

在图9-7VirtualBox窗口左边选中DOS客户机,点击工具栏中的“设置”按钮,选择软驱,选中“加载软驱”,主机上的软驱设为“B:”。

9-7 VirtualBox主窗口界面

9-8 设置DOS虚拟机的软驱

VirtualBox窗口(图9-7)中选择DOS客户机,点击工具栏中的“启动”按钮,启动进入DOS界面。此时,DOS客户机显示为“A>”,这里驱动器A中的文件系统和宿主机B:中完全一致。可以在Windows中编辑修改程序、编译连接,如图9-1所示。

9-9 Windows中编译、连接real2pro.asm

程序编译通过后,用鼠标左键点击DOS客户机窗口,在DOS客户机中执行real2pro.exe,结果如图9-1所示。

程序出现错误时,可执行菜单“虚拟电脑”“重启”,重新启动DOS客户机。

需要切换到Windows环境时,按键盘右边的Ctrl键即可。

要关闭DOS客户机,选择菜单“虚拟电脑”“关闭”,再在对话框中选择“强制关机”。

9.2 实模式与保护模式的切换

程序中定义了两个缓冲区:BufferBuffer2,从实模式切换到保护模式,将Buffer中的全部64个字节复制到Buffer2中,再回到实模式下,将Buffer2中的内容显示出来。

在程序的数据段DSEG中,定义了一个GDT,其中包括4个段描述符:空、代码段、数据段D、数据段EVGDTR 6个字节,用于存放GDTR的值。

BufferBuffer2分别位于数据段DSEGESEG中。

程序首先在实模式下执行,执行过程为:

(1)      设置3个段描述符(代码段、数据段D、数据段E)的基地址,这些段描述符的其他字段(限长、属性等)已经在定义时进行了赋值。

(2)      加载GDTR,使CPU在保护模式下能够访问GDT

(3)      在实模式时,A20门处于关闭状态。调用EnableA20宏将A20门打开,A20门打开时,CPU产生的A20地址有效,A20门关闭时,A20地址线总是为0

(4)      CR0的第0位置1,跳转到保护模式的Virtual标号,进入保护模式下执行。

在保护模式下,执行过程为:

(1)      DSES赋值。DS=0010HSS=0018H,分别指向数据段DSEGESEG

(2)      将数据段DSEGBuffer复制到ESEGBuffer2

(3)      CR0的第0位清0,跳转到实模式的Real标号,回到实模式下执行。

在实模式下,显示出Buffer2的内容。

;程序清单: real2pro.asm(实模式与保护方式之间的切换)

.386P

;存储段描述符结构类型定义

Desc            STRUC

LimitL          DW      0 ;段界限(BIT0-15)

BaseL           DW      0 ;段基地址(BIT0-15)

BaseM           DB      0 ;段基地址(BIT16-23)

Attributes      DB      0 ;段属性

LimitH          DB      0 ;段界限(BIT16-19)(含段属性的高4)

BaseH           DB      0 ;段基地址(BIT24-31)

Desc            ENDS

 

;伪描述符结构类型定义(用于装入全局或中断描述符表寄存器)

PDesc           STRUC

Limit           DW      0 ;16位界限

Base            DD      0 ;32位基地址

PDesc           ENDS

 

;存储段描述符类型值说明

ATDR            EQU     90h ;存在的只读数据段类型值

ATDW            EQU     92h ;存在的可读写数据段属性值

ATDWA           EQU     93h ;存在的已访问可读写数据段类型值

ATCE            EQU     98h ;存在的只执行代码段属性值

ATCER           EQU     9ah ;存在的可执行可读代码段属性值

ATCCO           EQU     9ch ;存在的只执行一致代码段属性值

ATCCOR          EQU     9eh ;存在的可执行可读一致代码段属性值

 

DSEG            SEGMENT USE16                 ;16位数据段

GDT             LABEL   BYTE                  ;全局描述符表

DUMMY           Desc    <>                    ;空描述符

Code            Desc    <0ffffh,,,ATCE,,>     ;代码段描述符

DataD           Desc    <0ffffh,0,,ATDW,,>    ;源数据段描述符

DataE           Desc    <0ffffh,,,ATDW,,>     ;目标数据段描述符

GDTLen          =       $-GDT                 ;全局描述符表长度

VGDTR           PDesc   <GDTLen-1,>           ;伪描述符

Code_Sel        =       Code-GDT              ;代码段选择子

DataD_Sel       =       DataD-GDT             ;源数据段选择子

DataE_Sel       =       DataE-GDT             ;目标数据段选择子

BufLen          =       64                    ;缓冲区字节长度

Buffer          DB      BufLen DUP(55h)       ;缓冲区

DSEG            ENDS                          ;数据段定义结束

 

ESEG            SEGMENT USE16                 ;16位数据段

Buffer2         DB      BufLen DUP(0)         ;缓冲区

ESEG            ENDS                          ;数据段定义结束

 

SSEG            SEGMENT PARA STACK            ;16位堆栈段

                DB      512 DUP (0)

SSEG            ENDS                          ;堆栈段定义结束

 

;打开A20地址线

EnableA20       MACRO

                push    ax

                in      al,92h

                or      al,00000010b

                out     92h,al

                pop     ax

                ENDM

 

;关闭A20地址线

DisableA20      MACRO

                push    ax

                in      al,92h

                and     al,11111101b

                out     92h,al

                pop     ax

                ENDM

 

;字符显示宏指令的定义

EchoCh          MACRO   ascii

                mov     ah,2

                mov     dl,ascii

                int     21h

                ENDM

 

;16位偏移的段间直接转移指令的宏定义(16位代码段中使用)

JUMP16          MACRO   Selector,Offset

                DB      0eah     ;操作码

                DW      Offset   ;16位偏移量

                DW      Selector ;段值或段选择子

                ENDM

 

CSEG            SEGMENT USE16                 ;16位代码段

                ASSUME  CS:CSEG,DS:DSEG

Start           PROC

                mov     ax,DSEG

                mov     ds,ax

                ;准备要加载到GDTR的伪描述符

                mov     bx,16

                mul     bx

                add     ax,OFFSET GDT          ;计算并设置基地址

                adc     dx,0                   ;界限已在定义时设置好

                mov     WORD PTR VGDTR.Base,ax

                mov     WORD PTR VGDTR.Base+2,dx

                ;设置代码段描述符

                mov     ax,cs

                mul     bx

                mov     WORD PTR Code.BaseL,ax ;代码段开始偏移为0

                mov     BYTE PTR Code.BaseM,dl ;代码段界限已在定义时设置好

                mov     BYTE PTR Code.BaseH,dh

                ;设置源数据段描述符

                mov     ax,ds

                mul     bx

                mov     WORD PTR DataD.BaseL,ax

                mov     BYTE PTR DataD.BaseM,dl

                mov     BYTE PTR DataD.BaseH,dh

                ;设置目标数据段描述符

                mov     ax,ESEG

                mul     bx

                mov     WORD PTR DataE.BaseL,ax

                mov     BYTE PTR DataE.BaseM,dl

                mov     BYTE PTR DataE.BaseH,dh

                ;加载GDTR

                lgdt    QWORD PTR VGDTR

                cli                            ;关中断

                EnableA20                      ;打开地址线A20

               

                ;切换到保护方式

                mov     eax,cr0

                or      eax,1

                mov     cr0,eax

               

                ;清指令预取队列,并真正进入保护方式

                JUMP16  Code_Sel,<OFFSET Virtual>

Virtual:       

                ;现在开始在保护方式下运行

                mov     ax,DataD_Sel

                mov     ds,ax                  ;加载源数据段描述符

                mov     ax,DataE_Sel

                mov     es,ax                  ;加载目标数据段描述符

                cld

                lea     esi,Buffer

                lea     edi,Buffer2            ;设置指针初值

                mov     ecx,BufLen/4           ;设置传送次数

                repz    movsd                  ;传送

               

                ;切换回实模式

                mov     eax,cr0

                and     al,11111110b

                mov     cr0,eax

               

                ;清指令预取队列,进入实方式

                JUMP16  <SEG Real>,<OFFSET Real>

Real:          

                ;现在又回到实方式

                DisableA20

                sti

 

                mov     ax,DSEG

                mov     ds,ax

                mov     ax,ESEG

                mov     es,ax

               

                mov     di,OFFSET Buffer2

                cld

                mov     bp,BufLen/16

NextLine:       mov     cx,16

NextCh:         mov     al, es:[di]

                inc     di

                push    ax

                shr     al,4

                call    ToASCII

                EchoCh  al

                pop     ax

                call    ToASCII

                EchoCh  al

                EchoCh  ' '

                loop    NextCh

                EchoCh  0dh

                EchoCh  0ah

                dec     bp

                jnz     NextLine

 

                mov     ax,4c00h

                int     21h

Start           ENDP

 

ToASCII         PROC

                and     al,0fh

                cmp     al,10

                jae     Over10

                add     al,'0'

                ret

Over10:

                add     al,'A'-10

                ret

ToASCII         ENDP

 

CSEG            ENDS                           ;代码段结束

                END     Start

9.3 保护模式下的中断程序设计

在响应中断时,CPU根据中断类型号执行对应的处理程序,把中断类型号作为中断描述符表IDT中描述符的索引,取得一个描述符(中断门),从描述符中得到中断处理程序的入口地址。中断描述符表寄存器IDTR指示IDT在内存中的位置和大小。

1. 中断处理程序

在程序的数据段DSEG中,定义了一个IDT,其中包括129个门描述符(IDT00~IDT80)。本实验中编写了3个中断处理程序:

(1)      IRQ0Handler:中断类型为20h,处理来自于8259的定时中断,每秒执行18.2次。

(2)      IRQ1Handler:中断类型为21h,处理来自于8259的键盘中断,键盘上的按键动作产生键盘中断。

(3)      UserIntHandler:中断类型为80h,处理由int 80h指令所产生的软件中断。

中断处理程序位于32位代码段中,在GDT中为它定义了一个段描述符,代码段的选择符(Code32_Sel)保存在中断门中。

2. 中断类型

系统中有28259,主片8259处理IRQ0~IRQ7,从片8259处理IRQ8~IRQ15。在实模式下,DOS操作系统将IRQ0~IRQ7的中断类型设为08h~0FHIRQ8~IRQ15的中断类型设为70h~77H。在保护模式下,08h~0FH类型号由CPU使用,所以本实验将IRQ0~IRQ7的中断类型设为20H~27H。需要对8259进行编程,来设定主片8259ICW2

8259初始化时,必须对ICW1~ICW4全部设置。

进入保护模式后,调用Init8259A设置主片8259、从片8259ICW1~ICW4,中断屏蔽寄存器;在返回实模式前,调用SetRealmode8259A恢复8259的设置。

3. 屏幕缓冲区

程序设置了一个段描述符用于访问屏幕缓冲区,屏幕缓冲区位于内存000B8000H处,屏幕上的每个字符在缓冲区占2个字节,第1个字节存放字符的ASCII码,第2个字节存放字符的属性(前景颜色、背景颜色)。

屏幕一共有25行、每一行80个字符,位置(x,y)所对应的缓冲区地址为:

000B8000h + (y * 80 + x) * 2                   (x=0-24, y=0-79)

描述符中,基地址设为000B8000h,将其选择符DataV_Sel赋给GSGS:[(y * 80 + x) * 2]对应于位置(x,y)所在的字符,GS:[(y * 80 + x) * 2 + 1]对应于该字符的显示属性。

4. 执行过程

中断处理程序执行时,必须要用到堆栈段,因此,程序将SS设为DataP_Sel,与数据段共用一个选择符。

中断程序所实现的功能为:

(1)      IRQ0Handler程序改变屏幕上(0,70)位置的字符,程序执行时,该位置的字符不停地改变;

(2)      IRQ1Handler程序改变屏幕上(1,70)位置的字符,有键盘输入时,该位置的字符发生改变;输入的扫描码保存在inkey变量中;

(3)      UserIntHandler在屏幕(2,70)位置显示一个字符“!”。在保护模式下,执行指令“int 80h”调用该程序。

IRQ0IRQ1是来自于主片8259的硬件中断,在中断处理程序中必须执行EOIEnd Of Interrupt)操作,向主片825920h端口发送数据20h

输入Esc键时,Esc键的扫描码01H被保存在inkey中,使程序从保护模式的循环查询中退出。

进入保护模式之前,SSSPIDTR的值被保存在数据段中,回到实模式后,恢复这3个寄存器的值。

;程序清单: intrpt.asm(保护方式下的中断处理过程)

.386P

;存储段描述符结构类型定义

Desc            STRUC

LimitL          DW      0 ;段界限(BIT0-15)

BaseL           DW      0 ;段基地址(BIT0-15)

BaseM           DB      0 ;段基地址(BIT16-23)

Attributes      DB      0 ;段属性

LimitH          DB      0 ;段界限(BIT16-19)(含段属性的高4)

BaseH           DB      0 ;段基地址(BIT24-31)

Desc            ENDS

 

;伪描述符结构类型定义(用于装入全局或中断描述符表寄存器)

PDesc           STRUC

Limit           DW      0 ;16位界限

Base            DD      0 ;32位基地址

PDesc           ENDS

 

;门描述符结构类型定义

Gate            STRUC

OffsetL         DW      0 ;32位偏移的低16

Selector        DW      0 ;选择符

DCount          DB      0 ;双字计数

GType           DB      0 ;类型

OffsetH         DW      0 ;32位偏移的高16

Gate            ENDS

 

;存储段描述符类型值说明

ATDR            EQU     90h ;存在的只读数据段类型值

ATDW            EQU     92h ;存在的可读写数据段属性值

ATDWA           EQU     93h ;存在的已访问可读写数据段类型值

ATCE            EQU     98h ;存在的只执行代码段属性值

ATCER           EQU     9ah ;存在的可执行可读代码段属性值

ATCCO           EQU     9ch ;存在的只执行一致代码段属性值

ATCCOR          EQU     9eh ;存在的可执行可读一致代码段属性值

DA_386IGate EQU 8Eh ;386 中断门类型值

 

DSEG            SEGMENT USE16         ;16位数据段

 

GDT             LABEL   BYTE          ;全局描述符表

DUMMY           Desc    <>            ;空描述符

Code            Desc    <0ffffh,,,ATCER,,>    ;代码段描述符

DataV           Desc    <0ffffh,,,ATDW,,>     ;数据段描述符(屏幕缓冲区)

DataP           Desc    <0ffffh,,,ATDWA,,>    ;数据段描述符

Code32          Desc    <0ffffh,,,ATCER,40h,> ;代码段描述符

 

GDTLen          =       $-GDT         ;全局描述符表长度

VGDTR           PDesc   <GDTLen-1,>           ;伪描述符

 

; IDT

ALIGN 32

IDT             LABEL   BYTE

IDT_00_1F Gate 32 dup (<offset SpuriousHandler,Code32_Sel,0,DA_386IGate,0>)

IDT_20    Gate 1  dup (<offset IRQ0Handler,Code32_Sel,0,DA_386IGate,0>)

IDT_21    Gate 1  dup (<offset IRQ1Handler,Code32_Sel,0,DA_386IGate,0>)

IDT_22_7F Gate 94 dup (<offset SpuriousHandler,Code32_Sel,0,DA_386IGate,0>)

IDT_80    Gate 1  dup (<offset UserIntHandler,Code32_Sel,0,DA_386IGate,0>)

 

IDTLen          =       $-IDT         ;中断描述符表长度

VIDTR           PDesc   <IDTLen-1,>   ;伪描述符

 

_SavedSP        dw      0

_SavedSS        dw      0

_SavedIDTR      dd      0       ; 用于保存 IDTR

                dd      0

               

DSEG            ENDS            ;数据段定义结束

 

PSEG            SEGMENT PARA STACK      ;保护模式下使用的数据段

                db      512 dup (0)

TopOfStack      LABEL   BYTE

 

inkey           db      0

_tmp            db      0               

 

_SavedIMREG_M   db      0           ; 中断屏蔽寄存器值

_SavedIMREG_S   db      0           ;

PSEG            ENDS

 

SSEG            SEGMENT PARA STACK      ;16位堆栈段

                DB      512 DUP (0)

SSEG            ENDS                    ;堆栈段定义结束

 

Code_Sel        =       Code-GDT        ;16位代码段选择符

DataV_Sel       =       DataV-GDT       ;屏幕缓冲区数据段选择符

DataP_Sel       =       DataP-GDT       ;PSEG数据段选择符

Code32_Sel      =       Code32-GDT      ;32位代码段段选择符

 

;打开A20地址线

EnableA20       MACRO

        push    ax

        in      al,92h

        or      al,00000010b

        out     92h,al

        pop     ax

        ENDM

 

;关闭A20地址线

DisableA20      MACRO

        push    ax

        in      al,92h

        and     al,11111101b

        out     92h,al

        pop     ax

        ENDM

 

;16位偏移的段间直接转移指令的宏定义(16位代码段中使用)

JUMP16  MACRO   Selector,Offset

        DB      0eah     ;操作码

        DW      Offset   ;16位偏移量

        DW      Selector ;段值或段选择符

        ENDM

 

CSEG    SEGMENT USE16         ;16位代码段

        ASSUME  CS:CSEG,DS:DSEG

 

Start   PROC

        mov     ax,DSEG

        mov     ds,ax

        mov     _SavedSP,ss

        mov     _SavedSS,sp

 

        ;准备要加载到GDTR的伪描述符

        mov     bx,16

        mul     bx

        add     ax,OFFSET GDT           ;计算并设置基地址

        adc     dx,0                    ;界限已在定义时设置好

        mov     WORD PTR VGDTR.Base,ax

        mov     WORD PTR VGDTR.Base+2,dx

 

        ;准备要加载到IDTR的伪描述符

        mov     ax,SEG IDT

        mov     bx,16

        mul     bx

        add     ax,OFFSET IDT           ;计算并设置基地址

        adc     dx,0                    ;界限已在定义时设置好

        mov     WORD PTR VIDTR.Base,ax

        mov     WORD PTR VIDTR.Base+2,dx

 

        ;设置代码段描述符

        mov     ax,cs

        mul     bx

        mov     WORD PTR Code.BaseL,ax  ;代码段开始偏移为0

        mov     BYTE PTR Code.BaseM,dl  ;代码段界限已在定义时设置好

        mov     BYTE PTR Code.BaseH,dh

 

        ;设置代码段描述符(32位代码段)

        mov     ax,seg SpuriousHandler

        mul     bx

        mov     WORD PTR Code32.BaseL,ax ;代码段开始偏移为0

        mov     BYTE PTR Code32.BaseM,dl ;代码段界限已在定义时设置好

        mov     BYTE PTR Code32.BaseH,dh

 

        ;设置数据段描述符(屏幕显示缓冲区)

        mov     ax,8000h

        mov     dx,000BH

        mov     WORD PTR DataV.BaseL,ax

        mov     BYTE PTR DataV.BaseM,dl

        mov     BYTE PTR DataV.BaseH,dh

 

        ;设置数据段描述符(保护模式下使用的数据段)

        mov     ax,PSEG

        mul     bx             ;计算并设置数据段基址

        mov     WORD PTR DataP.BaseL,ax

        mov     BYTE PTR DataP.BaseM,dl

        mov     BYTE PTR DataP.BaseH,dh

 

        ; 保存 IDTR

        sidt    QWORD PTR _SavedIDTR

 

        ;加载GDTR

        lgdt    QWORD PTR VGDTR

        cli                    ;关中断

        EnableA20              ;打开地址线A20

 

        lidt    QWORD PTR VIDTR

       

        ;切换到保护方式

        mov     eax,cr0

        or      eax,1

        mov     cr0,eax

        ;清指令预取队列,并真正进入保护方式

        JUMP16  Code_Sel,<OFFSET Virtual>

 

ALIGN 32

Virtual:        ;现在开始在保护方式下运行

        mov     ax,DataV_Sel

        mov     gs,ax          ;GS指向屏幕显示缓冲区

        mov     ax,DataP_Sel

        mov     ds,ax          ;DS指向PSEG

        mov     ss,ax          ;SS指向PSEG

        mov     sp,offset TopOfStack

        

        ; 保存中断屏蔽寄存器(IMREG)

        in      al,21h

        mov     _SavedIMREG_M,al

       

        in      al,0A1h

        mov     _SavedIMREG_S,al

 

        call    Init8259A

 

        int     080h

       

        sti

WaitLoop:

        mov     al,inkey

        mov     _tmp,al  

        cmp     _tmp,1

        jnz     WaitLoop

       

        cli

 

        call    SetRealmode8259A

 

        ;切换回实模式

        mov     eax,cr0

        and     al,11111110b

        mov     cr0,eax

        ;清指令预取队列,进入实方式

        JUMP16  <SEG Real>,<OFFSET Real>

 

Init8259A:

        mov     al,011h

        out     020h,al    ; 8259,ICW1.

        call    io_delay

           

        out     0A0h,al    ; 8259,ICW1.

        call    io_delay

           

        mov     al,020h    ; IRQ0 对应中断向量 0x20

        out     021h,al    ; 8259,ICW2.

        call    io_delay

           

        mov     al,028h    ; IRQ8 对应中断向量 0x28

        out     0A1h,al    ; 8259,ICW2.

        call    io_delay

           

        mov     al,004h    ; IR2 对应从8259

        out     021h,al    ; 8259,ICW3.

        call    io_delay

           

        mov     al,002h    ; 对应主8259 IR2

        out     0A1h,al    ; 8259,ICW3.

        call    io_delay

           

        mov     al,001h

        out     021h,al    ; 8259,ICW4.

        call    io_delay

           

        out     0A1h,al    ; 8259,ICW4.

        call    io_delay

           

        mov     al,11111100b   ; 仅仅开启定时器、键盘中断

        out     021h,al        ; 8259,OCW1.

        call    io_delay

            

        mov     al,11111111b   ; 屏蔽从8259所有中断

        out     0A1h,al        ; 8259,OCW1.

        call    io_delay

           

        ret

       

SetRealmode8259A:

        mov     al,011h

        out     020h,al    ; 8259,ICW1.

        call    io_delay

           

        out     0A0h,al    ; 8259,ICW1.

        call    io_delay

           

        mov     al,08h     ; IRQ0 对应中断向量 0x20

        out     021h,al    ; 8259,ICW2.

        call    io_delay

           

        mov     al,70h     ; IRQ8 对应中断向量 0x28

        out     0A1h,al    ; 8259,ICW2.

        call    io_delay

           

        mov     al,004h    ; IR2 对应从8259

        out     021h,al    ; 8259,ICW3.

        call    io_delay

           

        mov     al,002h    ; 对应主8259 IR2

        out     0A1h,al    ; 8259,ICW3.

        call    io_delay

           

        mov     al,001h

        out     021h,al    ; 8259,ICW4.

        call    io_delay

           

        out     0A1h,al    ; 8259,ICW4.

        call    io_delay

            

        mov     al,_SavedIMREG_M      ; 恢复中断屏蔽寄存器(IMREG)的原值

        out     021h,al        ;

        call    io_delay

 

        mov     al,_SavedIMREG_S      ; 恢复中断屏蔽寄存器(IMREG)的原值

        out     0A1h,al        ;

        call    io_delay

 

        ret

       

io_delay:

        nop

        nop

        nop

        nop

        ret

       

Real:           ;现在又回到实方式

        DisableA20

 

        mov     ax,DSEG

        mov     ds,ax

        mov     ss,_SavedSP

        mov     sp,_SavedSS

 

        lidt    QWORD PTR _SavedIDTR

 

        sti

 

        mov     ax,4c00h

        int     21h

Start   ENDP

 

CSEG    ENDS                   ;代码段定义结束

 

CSEG32  SEGMENT USE32

        ASSUME CS:CSEG32,DS:PSEG

IRQ0Handler:

        inc     byte ptr gs:[((80 * 0 + 70) * 2)] ; 屏幕第 0 , 70 列。

        mov     al,20h

        out     20h,al                            ; 发送EOI到主8259

        iretd

 

IRQ1Handler:

        in      al,60h

        mov     inkey,al

        inc     byte ptr gs:[((80 * 1 + 70) * 2)] ; 屏幕第 1 , 70 列。

        mov     al,20h

        out     20h,al                            ; 发送EOI到主8259

        iretd

 

UserIntHandler:

        mov     ah,0Ch                            ; 0000 黑底 1100 红字

        mov     al,'I'

        mov     gs:[((80 * 2 + 70) * 2)],ax       ; 屏幕第 2 , 70 列。

        iretd

 

SpuriousHandler:

        iretd

CSEG32  ENDS

        END     Start

9.3 实验题:保护模式综合实验

intrpt.asm的基础上,扩充其GDTIDT表,修改中断类型号,显示键盘按键的ASCII码等。

要求:

1.    在保护模式下使用独立的堆栈段(与数据段区分开);

2.    设置一个新的中断类型81h,在保护模式下,执行“int 81h”将屏幕内容清空;

3.    将主片8259IRQ0~IRQ7中断类型号修改为50h~57h,从片IRQ8~IRQ15的中断类型号修改为58h~5fh

4.    键盘上有键被按下时,在屏幕上显示出ASCII码。表9-1列出了键盘上主要按键的扫描码与ASCII字符的对应关系。

9-1 扫描码与ASCII字符对应关系

扫描码

ASCII字符

扫描码

ASCII字符

扫描码

ASCII字符

扫描码

ASCII字符

02H

1

10H

q

1EH

a

2CH

z

03H

2

11H

w

1FH

s

2DH

x

04H

3

12H

e

20H

d

2EH

c

05H

4

13H

r

21H

f

2FH

v

06H

5

14H

t

22H

g

30H

b

07H

6

15H

y

23H

h

31H

n

08H

7

16H

u

24H

j

32H

m

09H

8

17H

i

25H

k

33H

,

0AH

9

18H

o

26H

l

34H

.

0BH

0

19H

p

27H

;

35H

/

0CH

-

1AH

[

28H

 

 

0DH

=

1BH

]

29H

`