实验9 保护模式编程基础

来源:互联网 发布:网络剧浪漫星星演员表 编辑:程序博客网 时间:2024/06/14 00:10
导读:
  新一篇: 实验10 硬盘DMA编程
  实验9 保护模式编程基础
  在保护模式下,可寻址高达4GB(甚至更多)的物理地址空间;支持存储器分段管理机制和分页管理机制;支持多任务;支持4个特权级和配套的特权检查机制,区分不同级别的代码。操作系统(如Windows、Linux等)正是依赖于这些特性来实现虚拟内存、内核/用户模式、多任务等功能。
  9.1 虚拟机开发环境
  实模式与保护模式的切换,以及保护模式下的中断、DMA处理等程序必须在特权级0下执行,而Windows应用程序在特权级3环境中运行,只有系统内核和设备驱动程序才能运行在特权级0。因此,这些实验需要在DOS环境下进行。
  借助于虚拟机技术,可以在Windows环境下完成这些实验。图9-1就是在虚拟机中运行DOS操作系统和real2pro.exe的结果。
  
  
  图9-1 在虚拟机中完成保护模式实验
  1. 虚拟机
  虚拟机软件可在一台计算机(称为宿主机)上模拟出若干台计算机,模拟出的计算机(称为客户机)都有自己单独的硬件配置,可以安装单独的操作系统。例如,宿主计算机运行Windows操作系统,在上面运行虚拟机软件,虚拟出的计算机可以运行Linux操作系统。这2个操作系统各自独立运行,互不干扰。
  VirtualBox是德国InnoTek公司开发的虚拟机软件,可以运行在Windows和Linux上,客户机上可以安装Windows、DOS、Linux、OpenBSD等操作系统。除了VirtualBox软件以外,常用的虚拟机软件还有VMWare、Virtual PC等。
  2. 建立虚拟软驱
  在保护模式实验中,宿主机运行Windows,客户机运行DOS。DOS和Windows之间的共享采用虚拟软驱来完成。
  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.img;Disk 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-7的VirtualBox窗口左边选中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 实模式与保护模式的切换
  程序中定义了两个缓冲区:Buffer和Buffer2,从实模式切换到保护模式,将Buffer中的全部64个字节复制到Buffer2中,再回到实模式下,将Buffer2中的内容显示出来。
  在程序的数据段DSEG中,定义了一个GDT,其中包括4个段描述符:空、代码段、数据段D、数据段E。VGDTR 占6个字节,用于存放GDTR的值。
  Buffer和Buffer2分别位于数据段DSEG和ESEG中。
  程序首先在实模式下执行,执行过程为:
  (1) 设置3个段描述符(代码段、数据段D、数据段E)的基地址,这些段描述符的其他字段(限长、属性等)已经在定义时进行了赋值。
  (2) 加载GDTR,使CPU在保护模式下能够访问GDT。
  (3) 在实模式时,A20门处于关闭状态。调用EnableA20宏将A20门打开,A20门打开时,CPU产生的A20地址有效,A20门关闭时,A20地址线总是为0。
  (4) 将CR0的第0位置1,跳转到保护模式的Virtual标号,进入保护模式下执行。
  在保护模式下,执行过程为:
  (1) 为DS、ES赋值。DS=0010H,SS=0018H,分别指向数据段DSEG和ESEG。
  (2) 将数据段DSEG的Buffer复制到ESEG的Buffer2。
  (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 伪描述符
  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,
  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 ,
  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. 中断类型
  系统中有2片8259,主片8259处理IRQ0~IRQ7,从片8259处理IRQ8~IRQ15。在实模式下,DOS操作系统将IRQ0~IRQ7的中断类型设为08h~0FH,IRQ8~IRQ15的中断类型设为70h~77H。在保护模式下,08h~0FH类型号由CPU使用,所以本实验将IRQ0~IRQ7的中断类型设为20H~27H。需要对8259进行编程,来设定主片8259的ICW2。
  对8259初始化时,必须对ICW1~ICW4全部设置。
  进入保护模式后,调用Init8259A设置主片8259、从片8259的ICW1~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赋给GS。GS:[(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”调用该程序。
  IRQ0和IRQ1是来自于主片8259的硬件中断,在中断处理程序中必须执行EOI(End Of Interrupt)操作,向主片8259的20h端口发送数据20h。
  输入Esc键时,Esc键的扫描码01H被保存在inkey中,使程序从保护模式的循环查询中退出。
  进入保护模式之前,SS、SP和IDTR的值被保存在数据段中,回到实模式后,恢复这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 伪描述符
  
  IDT
  ALIGN 32
  IDT LABEL BYTE
  IDT_00_1F Gate 32 dup ()
  IDT_20 Gate 1 dup ()
  IDT_21 Gate 1 dup ()
  IDT_22_7F Gate 94 dup ()
  IDT_80 Gate 1 dup ()
  
  IDTLen = $-IDT 中断描述符表长度
  VIDTR PDesc 伪描述符
  
  _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,
  
  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 ,
  
  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的基础上,扩充其GDT、IDT表,修改中断类型号,显示键盘按键的ASCII码等。
  要求:
  1. 在保护模式下使用独立的堆栈段(与数据段区分开);
  2. 设置一个新的中断类型81h,在保护模式下,执行“int 81h”将屏幕内容清空;
  3. 将主片8259的IRQ0~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 `
  
  

本文转自
http://blog.csdn.net/leapme/archive/2008/04/10/2279761.aspx
原创粉丝点击