⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 virtual.asm

📁 windows汇编语言程序设计 吴中平著 一书所附所有源代码。 该书重点讲解了Windows环境下汇编语言程序设计的基本原理及其实现过程
💻 ASM
📖 第 1 页 / 共 2 页
字号:
   and eax,0fffffffeh
   mov cr0,eax
   jmp_16 <seg dos_int>,<offset dos_int>
dos_int:
   mov ax,DATA_SEG
   mov ds,ax
   LSS sp,dword ptr Sp_Value
   LIDT  RealIdtr
   sti
   mov ah,6
   mov al,0
   mov bh,0; f & b Color
   mov cx,0
   mov dx,184fh
   int 10h  ; 清屏。
   mov ax,MainDataSeg
   mov es,ax
   mov ds,ax
   lea  bp,ExtMemMess
   mov dx,0h
   mov bh,0
   mov cx,EMM_LEN+48
   mov ah,13h
   mov al,1
   mov bl,034h
   int 10H
   cli
   mov ax,DATA_SEG
   mov ds,ax
   LIDT qword ptr V_Idtr
   mov eax,CR0
   or eax,1
   mov CR0,eax
   JMP_16 < DOSCODE_SEL>,<offset Dos_V>
Dos_V:
   mov ax,DOSSTACK_SEL
   mov ss,ax
   pop gs
   pop fs
   pop es
   pop ds
   popad
   IRETD
   jmp Input; 任务切换后,该地址保存于TSS的EIP成员中。
   DOSCODELENGTH=$
DosCodeSeg Ends
DosTSSSeg Segment para use16
   dd 0; trlink。
   dd ? ; esp0。
   dw ?,0; ss0。
   dd ? ; esp1。
   dw ?,0; ss1。
   dd ? ; esp2。
   dw ?,0; ss2。
   dd 0; cr3。
   dw Input,0; eip。
   dd 0; psw。
   dd 0; eax。
   dd 0; ecx。
   dd 0; edx。
   dd 0; ebx。
   dw DOSSTACKLENGTH,0; esp。
   dd 0; ebp。
   dd 0; esi。
   dd 0; edi。
   dw NORMAL_SEL,0; es。
   dw DOSCODE_SEL,0; cs。
   dw DOSSTACK_SEL,0 ; ss。
   dw NORMAL_SEL,0 ; ds。
   dw NORMAL_SEL,0 ; fs。
   dw NORMAL_SEL,0 ; gs。
   dw DOSLDT_SEL,0 ; ldt。
   dw 0
   dw $+2
   db 0ffh
   DOSTSSLENGTH=$
DosTSSSeg Ends
ControlSeg Segment PARA use16
   assume cs:ControlSeg
VirtualEntry:; 保护方式的入口。
; ===============================================================
; ①为保护方式下任务切换作准备。
   mov ax,MAINTSS_ALIAS_SEL; 别名。
   mov fs,ax
   mov dword ptr fs:Main_Tss.tresp0,MAINSTACK_POINTER_0
   mov  fs:Main_Tss.trss0,MAINSTACK_0_SEL
   mov dword ptr fs:Main_Tss.tresp2,MAINSTACK_POINTER_2
   mov  fs:Main_Tss.trss2,MAINSTACK_2_SEL; 设置栈项为特权级变化作准备。
   mov  fs:Main_Tss.trldt,MAINLDT_SEL
   mov ax,MAINDATA_SEL
   mov ds,ax
   mov ax,ECHO_BUFFER_SEL
   mov es,ax
   mov gs,ax
   mov ax,MAINSTACK_0_SEL
   mov ss,ax
   mov esp,MAINSTACK_POINTER_0
   mov ax,MAINTSS_SEL
   ltr ax
   mov ax,MAINLDT_SEL
   lldt ax
; 说明: 上段代码为任务切换和特权级转换提供初始化环境,无论何种情况发生,返回后都必须要再检测每个段寄存器内容,给段寄存器赋值的用途正在于此。
; ===============================================================
; ②1MB以外内存的存取。
   Call_16 VISITEXMEM_SEL,GetExMem
   int 21h; 显示结果。
; 说明: 在MAIN任务中访问扩展内存,在DOS任务中完成显示。任务由MAIN切换到DOS任务中。先检测DOSTSS任务门,在非普通代码段中,只有CPL(MAIN任务)≤DPL(DOSTSS任务门)才能进行切换,任务切换大致要经过以下几步:
; ①检测DOSTSS区大小,不得小于103个字节。
; ②保存MAIN任务的相关寄存器的值,LDTR、CR3寄存器的值须手动保存于MAINTSS中。但本例中由于原来已设置过MAINTSS的LDT成员值,故没有必要再手动保存。
; ③把MAINTSS的选择子自动保存在DOSTSS的TrLink中。
; ④装载相应的寄存器,通过任务门的检查就可转向该任务中。
; ⑤DOS任务执行完毕,根据NT位判断是否需要解链处理。如果NT=1,则用DOSTSS中的TrLink恢复MAINTSS状态。注意,在恢复状态前要检测相关段寄存器的值,这是① 中初始化段寄存器的目的之一。 
; ===============================================================
; ③任务内特权级的变化。
   push MAINSTACK_2_SEL
   push MAINSTACK_POINTER_2; 在0级堆栈中压入2级堆栈的SS: SP。
   push  LEVEL2_SEL
   push offset Level2; 在0级堆栈中压入2级返回的指令地址CS:IP。
   retf; 强制段间返回。
ReturnLevel_0_Entry:
   pop ebx; level2 eip。
   pop ebx; level2 32bit cs。
   pop edx; 12345678H。
   mov ax,MAINDATA_SEL
   mov es,ax
   lea di,Buffer_DPL
   mov cx,4
       toa_c:
        rol edx,8
    mov al,dl
    Call_16 MAINCODE_SEL,ToAscii; 转换为ASCII。
    stosw
    loop toa_c
    mov ax,MAINDATA_SEL
    mov ds,ax
    lea si,DPLConvert
    mov ax,ECHO_BUFFER_SEL
    mov es,ax
    mov di,160*3
    mov cx,DPL_LEN
    call_16 MAINCODE_SEL,Dis_play
    mov di,160*4; 换4行,下同。
    mov cx,8
    lea si,Buffer_DPL
    mov cx,8
        call_16 MAINCODE_SEL,Dis_play; 显示结果。
; 说明: 特权级变化是个复杂的过程。在本例中,程序进入保护方式后,CPL=0,要完成演示特权级变化的过程,需要让程序进入一个DPL=2的代码段中,即由特权级高向特权级低的方向转移。这种转移不可以通过CALL、JMP指令完成,必须要通过RET指令(包括从任务返回也是这样)。要营造一种由0级特权级向2级特权级转移的工作环境,通过RET返回,必须要在0级特权的堆栈中压入二级堆栈指针和程序要返回的地址,由于本例工作于16位方式,故只须压入16位值即可。
; retf指令执行后,程序转移到CodeSeg_2段的LeveL2入口处,这时CPL=2。
; 程序执行到Level2处时,实际上工作的堆栈也是二级堆栈。在二级堆栈中演示压入12345678H的值,然后通过CALL调用指令,转向ReturnLevel_0_Entry(DPL=0)处。这是一个典型的从CPL=2向DPL=0的由外层向内层转移过程。
; 要完成这个过程,必须满足以下几个条件:①只能通过调用门或任务切换才能完成由特权级由低向高的转移; ②必须通过特级级检查。检查的内容是CPL与调用门的DPL关系,只有CPL≤DPL才能通过检查; ③相应的堆栈指针必须设置好,即使不使用的堆栈; ④如果该过程要传递参数,必须在调用门描述符中说明(即双字计数器),本例为1。在本例中完成这个过程的步骤如下:
; ⑴在0级堆栈中保存2级堆栈的地址(32位)。
; ⑵复制双字计数器指定的参数,即12345678H。
; ⑶在0级堆栈中保存供返回用的特权二级的EIP、32位CS。
; 上述三步,与我们在③中开始处营造的环境相似。请注意,不管是16位段还是32位段,在向内层转换过程中的堆栈是以32位方式入栈的。
; 程序返回到0级代码处,即ReturnLevel_0_Entry处,为了取得当前堆栈(0级)中的12345678H,必须先弹出保存在堆栈中的二级EIP和CS,即程序中连续二个pop ebx,然后pop edx,弹出的就是从二级堆栈中复制过来的12345678H。
; 这时,您可能注意到,在保护方式下,好像维护堆栈平衡并不是一件十分重要的事情,的确如此!Windows操作系统就有这种特点。如本例中,二级堆栈中压入的12345678H就不平衡。但不管怎样,必须慎重对待堆栈中保存的返回IP(EIP)、CS。
; ===============================================================
; ④模拟异常发生,通过向只读数据段写而导致中断自然被动发生,即故障。
   mov ax,EXTMEM_SEL
   mov ds,ax
   mov dword ptr ds:[0],0
; 说明:  EXTMEM_SEL的段定义为只读,因此,凡是通过EXTMEM_SEL选择子来访问该段的程序必须都不能写。本例模拟向这个区域内写一个双字,将会发生通用保护故障(0DH类型)。在执行0DH类型的中断服务子程序时,先在堆栈中保存PSW、CS、EIP,它们都是32位(保护方式下的中断入栈都是32位)。显示相关信息后,程序要返回,返回的地址指令是“mov dword ptr ds:[0],0”,将产生无限循环。该指令为7个字节长,EIP=EIP+7保证中断服务子程序返回后的地址是⑤处。
; ===============================================================
; ⑤模拟异常发生,,主动调用。
   INT 0FFH    
; 说明: 该指令主动调用0FFH类型的中断服务子程序,不会产生出错码,并且在堆栈中保存的是下一条指令的地址,本例是⑥处,主动调用相当于一个陷阱。
; ===============================================================
; ⑥启动分页机制,通过陷阱门主动调用。
   mov ax,MAINDATA_SEL
   mov ds,ax
   lea si,PageManage
   mov ax,ECHO_BUFFER_SEL
   mov es,ax
   mov di,160*10
   mov cx,PAGE_LEN
   call_16 MAINCODE_SEL,Dis_play; 显示分页提示信息。
   INT 0FEH
; 说明: 该指令通过陷阱门进入分页演示,IRETD指令返回,这里再补充一些内容。
; ①先做好页目录表、页表、物理页之间的对应关系,如4MB以内的物理内存对应页目录表0表项,同时对应页表0,4MB~8MB的物理内存对应页目录表1表项,同时对应页表1,依次类推,切不可“张冠李戴”。
; ②1MB以内的物理内存,最好保证逻辑页与物理页完全对应,避免由实方式向保护方式转变之后通过分页机制出现寻址混乱的现象。
; ③本例中,执行分页处的代码(第16M内存处)后,系统自动把页表0的3FFH表项的属性A位改为1,这与3FEH表项是不同的。
ToReal:; 为进入实方式作准备。
   mov ax,NORMAL_SEL
   mov ds,ax
   mov es,ax
   mov fs,ax
   mov gs,ax
   mov ss,ax
   mov eax,cr0; 退出保护方式。
   and eax,0fffeh
   mov cr0,eax
   jmp FAR ptr ToDos
ControlSeg Ends
; 以下为实方式,初始化内容。
DATA_SEG Segment para use16 
V_Gdtr    G_IDC <GDTLENGTH-1,>; 全局描述符表。
V_Idtr    G_IDC <IDTLENGTH-1,>; 中断描述符表。
RealIdtr  G_IDC <3FFH,0>; 实方式下,中断表,0:0处。
Sp_Value  dw ?
Ss_Value  dw ?
DATA_SEG Ends
CODE_SEG Segment para use16
   assume ds:DATA_SEG,CS:CODE_SEG
start:
   mov ax,DATA_SEG
   mov ds,ax
   cld
   push ds
   call Init_Gdt; 初始化全局描述符表,即转化为实际的物理地址,下同。
   call Init_Idt; 初始化中断描述符表。
   assume ds:MainLDTSeg
   mov ax,MainLDTSeg
   mov ds,ax
   lea si,MainLDT_Init_Start
   mov cx,MAINLDT_INIT_NUM
   call Init_Ldt; 初始化MAIN任务局部描述符表。
   assume ds:DosLDTSeg
   mov ax,DosLDTSeg
   mov ds,ax
   mov cx,DOSLDT_INIT_NUM
   lea si,DosLDT_Init_Start
   call Init_Ldt; 初始化DOS任务局部描述符表。
   pop ds
   assume ds:data_seg
   mov Sp_Value,sp
   mov Ss_Value,ss
   LGDT V_Gdtr; GDTR
   SIDT RealIdtr; 保存实方式下的IDT,这条指令最好放在LGDT之后。
   cli
   LIDT V_Idtr; IDTR
   call     Enabled_A20; 打开21号地址线。
   mov eax,CR0
   or eax,1
   mov CR0,eax; 启动保护方式。
   JMP_16 <SWITCH_SEL>,<offset VirtualEntry>; 实方式下预取。
ToDos:
   call Disabled_A20; 关闭21号地址线。
   mov ax,DATA_SEG
   mov ds,ax 
   lss sp,dword ptr Sp_Value; 恢复实方式下的堆栈。
   LIDT RealIdtr; 恢复实方式下的IDTR。
   sti
   mov ax,DosDataSeg
   mov es,ax
   lea bp,DosMess
   mov dx,1700H
   mov bh,0
   mov cx,DOSMESSLEN
   mov al,0
   mov bl,0d2h
   mov ah,13h
   int 10h; 提示退出信息。
   mov ah,0
   int 16h
   mov ah,6
   mov al,0
   mov bh,7; f & b Color
   mov cx,0
   mov dx,184fh
   int 10h ; 清屏,恢复原屏幕。
   mov ax,4c00h
   int 21h
Init_Gdt proc near
   push ds
   assume ds:GDTSEG
   mov ax,GDTSEG
   mov ds,ax
   mov cx,GDT_INIT_NUM
   lea si,Gdt_Init_Start
repseg_gdt:
   mov ax,[si].basel
   mov bx,16
   mul bx   
   mov [si].basel,ax
   mov [si].basem,dl
   mov [si].baseh,dh
   add si,size DESCRIPTOR
   loop repseg_gdt
assume ds:data_seg
   pop ds
   mov bx,16
   mov ax,GDTSEG
   mul bx
   mov word ptr V_Gdtr.base,ax
   mov word ptr V_Gdtr.base+2,dx
   ret
Init_Gdt endp
Init_Ldt proc near
repseg_ldt:
   mov ax,[si].basel
   mov bx,16
   mul bx
   mov [si].basel,ax
   mov [si].basem,dl
   mov [si].baseh,dh
   add si,size descriptor
   loop repseg_ldt
   ret
Init_Ldt endp
Init_Idt proc    near
   mov ax,IDTSEG
   mov  bx,16
   mul bx
   mov word ptr V_Idtr.base,ax
   mov word ptr V_Idtr.base+2,dx
   ret
Init_Idt Endp
Enabled_A20 proc near; 打开A20。
   push ax
   in al,92h
   or al,2
   out 92h,al
   pop ax
   ret
Enabled_A20 endp
Disabled_A20 proc; 关闭A20。
   push ax
   in al,92h
   and al,0fdh
   out 92h,al
   pop ax
   ret
Disabled_A20 endp
CODE_SEG ends
end Start

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -