2005年01月11日

回顾1.7日的竞标,觉得平时就应注意文档的积累:

1.公司资料
2.公司主要产品报价
3.资料格式、排版
4.技术资料(图+文字说明)
(1)安全方面
(2)效率方面
(3)系统架构,如SOA等
(4)系统管理
(5)维护
(6)项目管理

一般竞标的方案建议书(计划承诺书)需要以下几个部分:
1.为什么这么做、系统设计原则
2.网络拓朴结构
3.软件结构
4.技术具体实现
5.系统硬件配置=>报价
6.系统管理
7.系统接口
8.其它客户要求的技术

演示、演讲时,
1.介绍自己
2.介绍公司
3.具体实现

2005年01月10日

2005.1.10
快半年了,以前看的又忘掉了,现再看一遍,觉得似曾相识,又摘了一部分(存储段描述符):

存储段
描述符
m+7 m+6 m+5 m+4 m+3 m+2 m+1 m+0
Base(31…24) Attributes Segment Base(23…0) Segment Limite(15…0)



存储段
描述符
属  性
Byte m+6 Byte m+5
BIT7 BIT6 BIT5 BIT4 BIT3 BIT2 BIT1 BIT0 BIT7 BIT6 BIT5 BIT4 BIT3 BIT2 BIT1 BIT0
G D 0 AVL Limit(19…16) P DPL DT1 TYPE

TYPE说明存储段描述符所描述的存储段的具体属性。具体解释如下:

数据段:

BIT3 BIT2 BIT1

BIT0

E=0(都可读) ED W A

ED=0:向高扩展
ED=1:向低扩展

W=0:不可写
W=1:可写

A=0:未访问
A=1:已方问

代码段:

BIT3 BIT2 BIT1

BIT0

E=1(不可写) C R A

E=1(不可写,若需要对代码段进行写入操作,则必须使用别名技术)

C=0:不是一致代码段(普通代码段)
C=1:是一致代码段。

R=0:不可读,只能执行
R=1:可读可执行

A=0:未访问
A=1:已方问


一个任务可使用的整个虚拟地址空间分为相等的两半,一半空间的描述符在全局描述符表中,另一半空间的描述符在局部描述符表中。由于全局和局部描述符表都可以包含多达8192个描述符,而每个描述符所描述的段的最大值可达4G字节,因此最大的虚拟地址空间可为:
4GB*8192*2=64MMB=64TB


选择子=>描述符=>段基地址,
段基地址 + 偏移 = 线性地址。

选择子中的描述符索引字段13位=>描述符表最多包含8192个描述符。由于每个描述符长8字节,屏蔽选择子低3位后的值就是选择子所指定的描述符在描述符表中的偏移,有一个特殊的选择子称为空(Null)选择子,它的Index=0,TI=0,而RPL字段可以为任意值。 空选择子有特定的用途,当用空选择子进行存储访问时会引起异常。空选择子是特别定义的, 它不对应于全局描述符表GDT中的第0个描述符,因此处理器中的第0个描述符总不被处理器访问,一般把它置成全0。但当TI=1时,Index为0的选择子不是空选择子, 它指定了当前任务局部描述符表LDT中的第0个描述符。

2004.9.19:
《加密与解密II》1-2章,
了解了
1.Windows与Unicode
2.保护模式、虚拟内存
3.代码指令:跳转指令、条件指令、浮点指令
4.逆向分析程序:函数调用
5.PE格式
看了《386保护模式编程》一<1>
1.虚拟地址空间(二维)=>线性地址空间(一维)=>物理地址空间

2004.9.23
《386保护模式编程》二<1><2>
<1>每个段定义三个参数:段基地址(Base Address)、段界限(Limit)和段属性(Attributes)
(1).段基地址规定线性地址空间中段的开始地址
(2).段界限规定段的大小
(3).段属性规定段的主要特性

<2>描述符可分为如下三类:存储段描述符、系统段描述符、门描述符(控制描述符)

(1).存储段描述符的格式
1.D(ED)堆栈扩展方向
2.段粒度G(1M/4G)
3.AVL:保留,为软件可利用位
4.P存在(Present)位在不在内存中
5.DPL(Descriptor Privilege level)特权级,
6.Type有四位,3:数据段/代码段 2:代码段是否是一致代码段 1:数据是否可写/代码是否可执行 0:有未访问过

(2).存储段描述符的结构类型表示
    DESC       STRUC
    LIMITL     DW      0 ;段界限低16位
    BASEL      DW      0 ;基地址低16位
    BASEM      DB      0 ;基地址中间8位
    ATTRIB     DB      0 ;段属性
    LIMITH     DB      0 ;段界限的高4位(包括段属性的高4位)
    BASEH      DB      0 ;基地址的高8位
    DESC       ENDS
10H,5678H,34H,98H,0C0H,12H

2004.9.24
《386保护模式编程》二<3>
(1)80386中有三种类型的描述符表:
全局描述符表GDT(Global Descriptor Table) 1张
局部描述符表LDT(Local Descriptor Table)  N张
中断描述符表IDT(Interrupt Descriptor Table) 1张

<4>段选择子
段选择子长16位:
高13位是描述符索引(Index),就是描述符在描述符表中的序号
第2位是TI(Table Indicator),引用描述符表指示位,指示从GDT/LDT中读取描述符;
最低两位RPL(Requested Privilege Level),是请求特权级,用于特权检查

(1).由于选择子中的描述符索引字段用13位表示,所以可区分8192个描述符。这也就是描述符表最多包含8192个描述符的原因。
(2).每个描述符长8字节,根据上表所示选择子的格式,屏蔽选择子低3位后所得的值就是选择子所指定的描述符在描述符表中的偏移,这可认为是安排选择子高13位作为描述符索引的原因。
(3).当使用选择子的RPL字段时,与[所访问段的特权级]比较的不只是CPL(当前特权级),而是CPL与RPL中更外层的特权级。
(4).有一个特殊的选择子称为空(Null)选择子,它的Index=0


2004.9.25
《386保护模式编程》三.<1>控制寄存器
80386有CR0、CR1、CR2和CR3四个32位的控制寄存器,
(1).
CR0中的位5—位30及CR3中的位0至位11是保留位,必须为0
CR1不能使用
CR2及CR3由分页管理机制使用
(2).CR0:
0 :P E 实模式/保护方式
31:PG 禁用/启用分页管理机制
PE=0且PG=1是无效组合

1:MP(算术存在位),忽略TS条件,不产生异常控制/WAIT指令在TS=1时,是产生DNA异常
2:EM(模拟位),浮点指令由硬件控制/软件模拟
3:TS(任务切换位),加快任务的切换
4:ET(扩展类型位),当处理器复位时,根据系统中不存在/存在 80387协处理器,ET位被初始化

2004.9.27
《386保护模式编程》三.<2>系统地址寄存器
为了方便快速地定位段GDT、LDT、IDT、TR,处理器采用系统地址寄存器保存这些段的基地址和段界限。

(1).全局描述符表寄存器GDTR:长48位,高32位:基地址,低16位:界限
1.由于GDT不能由GDT本身之内的描述符进行描述定义,所以处理器采用GDTR为GDT这一特殊的系统段提供一个伪描述符。GDTR给定了GDT。
2.界限(全局描述符表大小)为16位:由于段选择子中只有13位作为描述符索引,而每个描述符长8个字节,所以用16位的界限足够。对于含有N个描述符的描述符表的段界限设为8*N-1。

***(2).局部描述符表寄存器LDTR
1.LDTR类似于段寄存器,由程序员可见的16位的寄存器和程序员不可见的高速缓冲寄存器组成。
(3).中断描述符表寄存器IDTR
1.IDTR指示IDT的方式与GDTR指示GDT的方式相同
2.IDTR段界限为7FFH:由于80386只支持256个中断/异常,所以IDT表最大长度是2K(7FFH)
(4).任务状态段寄存器TR
1.TR也有程序员可见和不可见两部分。
2.装入到TR的选择子不能为空,必须索引位于GDT中的描述符,且描述符的类型必须是任务状态段描述符TSS

今天学习第二章(第一章好像没什么好记的,就算了吧)
刚一开始就看傻了,怎么全是很简要的说明呀?呜,只好找出光盘照着做:

(1)我装的是J2ee1.4,嗯?一开始就要设环境变量?JAVA_HOME是安装时就设好了,J2EE_HOME?好像没有这东东呀。ANT_HOME,找了找,原来在E:\Sun\AppServer\lib\ant这里,手动加上,然后APP也要加上ANT的BIN目录。看来应该找本1.4的Tutorial来看。

(2)嗯?j2ee -verbose?没有j2ee呀,呜,原来1.4版变成这样子了:
开始->程序-> Sun Microsystems -> Application Server -> Start Default Server.
而后面的J2ee -stop当然就变成:
开始->程序-> Sun Microsystems -> Application Server -> Start Default Server.

(3)搞了半天,终于有点步入正轨。调出了deploytool,有图形界面了,嘿嘿。
(4)后面的用ANT编译,好像又出错了,哎,看来真的要下载1.4版的了。暂时写到这里。

今天问猪猪老师的孩子在肚子里几个月了,答曰七个月。然后问了起什么名字,她说:暂定李梓翘楚,但觉得笔画太多了。然后我又建议李淑女、李俊男、李大淑女、李大俊男,最后想了一个“李木楚子”,嘿嘿,笔画少,会写“木”和“子”就可以了,又好听……

实例二的逻辑功能是,以十六进制数和ASCII字符两种形式显示从内存地址100000H开始的16个字节的内容。
从功能上看,本实例类似于实例一,但在实现方法上却有了改变,它更能反映出实模式和保护模式切换的情况。具体实现步骤是:(1)作切换到保护方式的准备;(2)切换到保护方式的一个32位代码段;(3)把指定内存区域的内容以字节为单位,转换成对应的十六进制数的ASCII码,并直接填入显示缓冲区实现显示;(4)再变换到保护方式下的一个16位代码段;(5)把指定内存区域的内容直接作为ASCII码填入显示缓冲区中实现显示;(6)切换回实模式。

1.实例二源程序

实例二的源程序如下所示:
;名称:ASM2.ASM
;功能:演示实方式和保护方式切换(切换到32位代码段)
;----------------------------------------------------------------------------
INCLUDE         386SCD.INC
;----------------------------------------------------------------------------
DSEG            SEGMENT USE16                     ;16位数据段
;----------------------------------------------------------------------------
GDT             LABEL   BYTE                      ;全局描述符表
DUMMY           Desc    <>                        ;空描述符
Normal          Desc    <0ffffh,,,ATDW,,>         ;规范段描述符
Code32          Desc    <C32Len-1,,,ATCE,D32,>    ;32位代码段描述符
Code16          Desc    <0ffffh,,,ATCE,,>         ;16位代码段描述符
DataS           Desc    <DataLen-1,0,10h,ATDR,,>  ;源数据段描述符
DataD           Desc    <3999,8000h,0bh,ATDW,,>   ;显示缓冲区描述符
Stacks          Desc    <StackLen-1,,,ATDW,,>     ;堆栈段描述符
;----------------------------------------------------------------------------
GDTLen          =       $-GDT                     ;全局描述符表长度
VGDTR           PDesc   <GDTLen-1,>               ;伪描述符
;----------------------------------------------------------------------------
SaveSP          DW      ?                         ;用于保存SP寄存器
SaveSS          DW      ?                         ;用于保存SS寄存器
;----------------------------------------------------------------------------
Normal_Sel      =       Normal-GDT                ;规范段描述符选择子
Code32_Sel      =       Code32-GDT                ;32位代码段选择子
Code16_Sel      =       Code16-GDT                ;16位代码段选择子
DataS_Sel       =       Datas-GDT                 ;源数据段选择子
DataD_Sel       =       DataD-GDT                 ;目标数据段选择子
Stacks_Sel      =       Stacks-GDT                ;堆栈段描述符选择子
;----------------------------------------------------------------------------
DataLen         =       16
;----------------------------------------------------------------------------
DSEG            ENDS                              ;数据段定义结束
;----------------------------------------------------------------------------
StackSeg        SEGMENT PARA STACK USE16
StackLen        =       256
                DB      StackLen DUP(0)
StackSeg        ENDS
;----------------------------------------------------------------------------
CSEG1           SEGMENT USE16 'REAL'              ;16位代码段
                ASSUME  CS:CSEG1,DS :D SEG
;----------------------------------------------------------------------------
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
                ;设置32位代码段描述符
                mov     ax,CSEG2
                mul     bx
                mov     WORD PTR Code32.BaseL,ax
                mov     BYTE PTR Code32.BaseM,dl
                mov     BYTE PTR Code32.BaseH,dh
                ;设置16位代码段描述符
                mov     ax,CSEG3
                mul     bx
                mov     WORD PTR Code16.BaseL,ax  ;代码段开始偏移为0
                mov     BYTE PTR Code16.BaseM,dl  ;代码段界限已在定义时设置好
                mov     BYTE PTR Code16.BaseH,dh
                ;设置堆栈段描述符
                mov     ax,ss
                mov     WORD PTR SaveSS,ax
                mov     WORD PTR SaveSP,sp
                mov     ax,StackSeg
                mul     bx
                mov     WORD PTR Stacks.BaseL,ax
                mov     BYTE PTR Stacks.BaseM,dl
                mov     BYTE PTR Stacks.BaseH,dh
                ;加载GDTR
                lgdt    QWORD PTR VGDTR
                cli                               ;关中断
                EnableA20                         ;打开地址线A20
                ;切换到保护方式
                mov     eax,cr0
                or      al,1
                mov     cr0,eax
                ;清指令预取队列,并真正进入保护方式
                JUMP16  Code32_Sel,<OFFSET SPM32>
ToReal:         ;现在又回到实方式
                mov     ax,DSEG
                mov     ds,ax
                mov     sp,SaveSP
                mov     ss,SaveSS
                DisableA20
                sti
                mov     ax,4c00h
                int     21h
Start           ENDP
;----------------------------------------------------------------------------
CSEG1           ENDS                              ;代码段定义结束
;----------------------------------------------------------------------------
CSEG2           SEGMENT USE32 'PM32'
                ASSUME  CS:CSEG2
;----------------------------------------------------------------------------
SPM32           PROC
                mov     ax,Stacks_Sel
                mov     ss,ax
                mov     esp,StackLen
                mov     ax,DataS_Sel
                mov     ds,ax
                mov     ax,DataD_Sel
                mov     es,ax
                xor     esi,esi
                xor     edi,edi
                mov     ecx,DataLen
                cld
Next:           lodsb
                push    ax
                CALL    ToASCII
                mov     ah,7
                shl     eax,16
                pop     ax
                shr     al,4
                CALL    ToASCII
                mov     ah,7
                stosd
                mov     al,20h
                stosw
                loop    Next
                JUMP32   Code16_Sel,<OFFSET SPM16>
SPM32           ENDP
;----------------------------------------------------------------------------
ToASCII         PROC
                and     al,00001111b
                add     al,30h
                cmp     al,39h
                jbe     Isdig
                add     al,7
IsDig:          ret
ToASCII         ENDP
;----------------------------------------------------------------------------
C32Len          =       $
;----------------------------------------------------------------------------
CSEG2           ENDS
;----------------------------------------------------------------------------
CSEG3           SEGMENT USE16 'PM16'
                ASSUME  CS:CSEG3
;----------------------------------------------------------------------------
SPM16           PROC
                xor     si,si
                mov     di,DataLen*3*2
                mov     ah,7
                mov     cx,DataLen
AGain:          lodsb
                stosw
                loop    AGain
                mov     ax,Normal_sel
                mov     ds,ax
                mov     es,ax
                mov     ss,ax
                mov     eax,cr0
                and     al,11111110b
                mov     cr0,eax
                jmp     FAR PTR ToReal
SPM16           ENDP
;----------------------------------------------------------------------------
CSEG3           ENDS
;----------------------------------------------------------------------------
                END     Start

2.关于实现步骤的注释

(1)切换到保护模式的准备工作

建立全局描述符表,这里的全局描述符表含有两个16位数据段的描述符、一个16位代码段的描述符和一个16位的堆栈段描述符。此外,GDT中还有一个32位的代码段描述符,描述32位代码段,该描述符的属性字段中的D位为1。

(2)由实模式切换到保护模式

由实模式切换到保护模式32位代码段的方法与切换到16位代码段的方法相同。由保护模式16位代码段切换回实模式的方法与实例一相似。
在保护模式下,通过如下直接段间转移指令从32位代码段切换到16位代码段:
    JUMP32   Code16_Sel,<OFFSET SPM16>
从该宏指令的定义可知,该转移指令含48位指针,其高16位是16位代码段的选择子,低32位是16位代码段的入口偏移。该指令在32位方式下预取并执行。由于在32位方式下执行,所以要使用48位指针。

(3)显示指定内存区域的内容

在本实例中,采用直接写显示缓冲区的方法实现显示。假设显示缓冲区的开始物理地址是0B8000H, 3号文本显示模式,在屏幕的第一行进行显示。

3.特别说明

本实例在保护方式下使用了涉及堆栈操作的指令,因此建立了一个16位的保护模式下的堆栈段。
本实例仍作了大量的简化处理。如:没有建立IDT和LDT等,各特权级均是0。也没有采用分页管理机制。
从本实例的GDT中可见,两个数据段的界限都是根据实际大小而设置的。从源程序代码段CSEG3可见,在切换到实模式之前,把一个指向似乎没有用的数据段的描述符Normal的选择子装载到DS和ES。这是为什么呢?



实模
式下
段描
述符
高速
缓冲
寄存
器的
内容
段寄存器 段基地址 段界限(固定) 段属性(固定)
存在性 特权级 已存取 粒度 扩展方向 可读性 可写性 可执行 堆栈大小 一致特权
CS 当前CS*16 0000FFFFH Y 0 Y B U Y Y Y - N
SS 当前SS*16 0000FFFFH Y 0 Y B U Y Y N W -
DS 当前DS*16 0000FFFFH Y 0 Y B U Y Y N - -
ES 当前ES*16 0000FFFFH Y 0 Y B U Y Y N - -
FS 当前FS*16 0000FFFFH Y 0 Y B U Y Y N - -
GS 当前GS*16 0000FFFFH Y 0 Y B U Y Y N - -


在分段管理机制一文中已介绍过,每个段寄存器都配有段描述符高速缓冲寄存器,这些高速缓冲寄存器在实方式下仍发挥作用,只是内容上与保护模式下有所不同。如上表所示,其中“Y”表示“是”; “N”表示“否”;“B”表示字节;“U”表示向上扩展,“W”表示以字方式操作堆栈。段基地址仍是 32位,其值是相应段寄存器值(段值)乘以16,在把段值装载到段寄存器时刷新。由于其值是16位段值乘上16,所以在实模式下基地址实际上有效位只有20位。每个段的32位段界限都固定为0FFFFH,段属性的许多位也是固定的。所谓固定是指在实方式下不可设置这些属性值,只能继续沿用保护方式下所设置的值。因此,在准备结束保护模式回到实模式之前,要通过加载一个合适的描述符选择子到有关段寄存器,以使得对应段描述符高速缓冲寄存器中含有合适的段界限和属性。本实例GDT中的描述符Normal就是这样一个描述符,在返回实模式之前把对应选择子Normal_Sel加载到DS和ES就是此目的。由于SS段描述符中的内容已符合实模式的需要,所以尽管也改变了SS,但不需要重新加载SS(本实例中重新加载了SS,这除了稍增加运行时间外,并没有什么坏处)。16位代码段描述符中的内容也符合实模式的需要,所以在通过16位代码段返回实模式时,CS段描述符中的内容也符合实模式的要求。需要注意的是,不能从32位代码段返回实模式,这是因为无法实现从32位代码段返回时CS高速缓冲寄存器中的属性符合实模式的要求(实模式不能改变段属性)。顺便说以下,实例一中的描述符都是符合实模式要求的。段描述符高速缓冲寄存器中含有合适的段界限

4.关于32位代码段程序设计的说明

在32位代码段中,缺省的操作数大小是32位,缺省的存储单元地址大小是32位。由于串操作指令使用的指针寄存器是ESI和EDI,LOOP指令使用的计数器是ECX,所以,在代码段CSEG2中,为了使用串操作指令,对ESI和EDI等寄存器赋初值。请比较代码段CSEG3中的相关片段和实例一中的相关片段,它们是16位代码段。

;名称:386SCD.INC
;功能:符号常量等的定义
;—————————————————————————-
;IFNDEF         __386SCD_INC
;__386SCD_INC   EQU     1
;—————————————————————————-
.386P
;—————————————————————————-
;打开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
;—————————————————————————-
;32位偏移的段间直接转移指令的宏定义(在32位代码段中使用)
;—————————————————————————-
COMMENT <JUMP32>
JUMP32          MACRO   Selector,Offset
                DB      0eah     ;操作码
                DD      OFFSET
                DW      Selector ;段值或段选择子
                ENDM
<JUMP32>
;————————————————-
JUMP32          MACRO   Selector,Offset
                DB      0eah     ;操作码
                DW      OFFSET
                DW      0
                DW      Selector ;段值或段选择子
                ENDM
;—————————————————————————-
;16位偏移的段间调用指令的宏定义(在16位代码段中使用)
;—————————————————————————-
CALL16          MACRO   Selector,Offset
                DB      9ah      ;操作码
                DW      Offset   ;16位偏移量
                DW      Selector ;段值或段选择子
                ENDM
;—————————————————————————-
;32位偏移的段间调用指令的宏定义(在32位代码段中使用)
;—————————————————————————-
COMMENT <CALL32>
CALL32          MACRO   Selector,Offset
                DB      9ah      ;操作码
                DD      Offset
                DW      Selector ;段值或段选择子
                ENDM
<CALL32>
;————————————————-
CALL32          MACRO   Selector,Offset
                DB      9ah      ;操作码
                DW      Offset
                DW      0
                DW      Selector ;段值或段选择子
                ENDM
;—————————————————————————-
;存储段描述符结构类型定义
;—————————————————————————-
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
;—————————————————————————-
;门描述符结构类型定义
;—————————————————————————-
Gate            STRUC
OffsetL         DW      0 ;32位偏移的低16位
Selector        DW      0 ;选择子
DCount          DB      0 ;双字计数
GType           DB      0 ;类型
OffsetH         DW      0 ;32位偏移的高16位
Gate            ENDS
;—————————————————————————-
;伪描述符结构类型定义(用于装入全局或中断描述符表寄存器)
;—————————————————————————-
PDesc           STRUC
Limit           DW      0 ;16位界限
Base            DD      0 ;32位基地址
PDesc           ENDS
;—————————————————————————-
;任务状态段结构类型定义
;—————————————————————————-
TSS             STRUC
TRLink          DW      0      ;链接字段
                DW      0      ;不使用,置为0
TRESP0          DD      0      ;0级堆栈指针
TRSS0           DW      0      ;0级堆栈段寄存器
                DW      0      ;不使用,置为0
TRESP1          DD      0      ;1级堆栈指针
TRSS1           DW      0      ;1级堆栈段寄存器
                DW      0      ;不使用,置为0
TRESP2          DD      0      ;2级堆栈指针
TRSS2           DW      0      ;2级堆栈段寄存器
                DW      0      ;不使用,置为0
TRCR3           DD      0      ;CR3
TREIP           DD      0      ;EIP
TREFlag         DD      0      ;EFLAGS
TREAX           DD      0      ;EAX
TRECX           DD      0      ;ECX
TREDX           DD      0      ;EDX
TREBX           DD      0      ;EBX
TRESP           DD      0      ;ESP
TREBP           DD      0      ;EBP
TRESI           DD      0      ;ESI
TREDI           DD      0      ;EDI
TRES            DW      0      ;ES
                DW      0      ;不使用,置为0
TRCS            DW      0      ;CS
                DW      0      ;不使用,置为0
TRSS            DW      0      ;SS
                DW      0      ;不使用,置为0
TRDS            DW      0      ;DS
                DW      0      ;不使用,置为0
TRFS            DW      0      ;FS
                DW      0      ;不使用,置为0
TRGS            DW      0      ;GS
                DW      0      ;不使用,置为0
TRLDTR          DW      0      ;LDTR
                DW      0      ;不使用,置为0
TRTrip          DW      0      ;调试陷阱标志(只用位0)
TRIOMap         DW      $+2    ;指向I/O许可位图区的段内偏移
TSS             ENDS
;—————————————————————————-
;存储段描述符类型值说明
;—————————————————————————-
ATDR            EQU     90h ;存在的只读数据段类型值
ATDW            EQU     92h ;存在的可读写数据段属性值
ATDWA           EQU     93h ;存在的已访问可读写数据段类型值
ATCE            EQU     98h ;存在的只执行代码段属性值
ATCER           EQU     9ah ;存在的可执行可读代码段属性值
ATCCO           EQU     9ch ;存在的只执行一致代码段属性值
ATCCOR          EQU     9eh ;存在的可执行可读一致代码段属性值
;—————————————————————————-
;系统段描述符类型值说明
;—————————————————————————-
ATLDT           EQU     82h ;局部描述符表段类型值
ATTaskGate      EQU     85h ;任务门类型值
AT386TSS        EQU     89h ;可用386任务状态段类型值
AT386CGate      EQU     8ch ;386调用门类型值
AT386IGate      EQU     8eh ;386中断门类型值
AT386TGate      EQU     8fh ;386陷阱门类型值
;—————————————————————————-
;DPL值说明
;—————————————————————————-
DPL0            EQU     00h ;DPL=0
DPL1            EQU     20h ;DPL=1
DPL2            EQU     40h ;DPL=2
DPL3            EQU     60h ;DPL=3
;—————————————————————————-
;RPL值说明
;—————————————————————————-
RPL0            EQU     00h ;RPL=0
RPL1            EQU     01h ;RPL=1
RPL2            EQU     02h ;RPL=2
RPL3            EQU     03h ;RPL=3
;—————————————————————————-
;IOPL值说明
;—————————————————————————-
IOPL0           EQU     0000h ;IOPL=0
IOPL1           EQU     1000h ;IOPL=1
IOPL2           EQU     2000h ;IOPL=2
IOPL3           EQU     3000h ;IOPL=3
;—————————————————————————-
;其它常量值说明
;—————————————————————————-
D32             EQU     40h       ;32位代码段标志
GL              EQU     80h       ;段界限以4K为单位标志
TIL             EQU     04h       ;TI=1(局部描述符表标志)
VMFL            EQU     00020000h ;VMF=1
VMFLW           EQU     0002h
IFL             EQU     00000200h ;IF=1
RFL             EQU     00010000h ;RF=1(重启动标志,为1表示忽略调试故障)
RFLW            EQU     0001h
NTL             EQU     00004000h ;NT=1
;—————————————————————————-
;分页机制使用的常量说明
;—————————————————————————-
PL              EQU     1     ;页存在属性位
RWR             EQU     0     ;R/W属性位值,读/执行
RWW             EQU     2     ;R/W属性位值,读/写/执行
USS             EQU     0     ;U/S属性位值,系统级
USU             EQU     4     ;U/S属性位值,用户级
;—————————————————————————-
;ENDIF

2.实例源程序
实例一的源程序如下所示:
;名称:ASM1.ASM
;功能:演示实方式和保护方式切换(切换到16位代码段)
;—————————————————————————-
INCLUDE         386SCD.INC
;—————————————————————————-
;字符显示宏指令的定义
;—————————————————————————-
EchoCh          MACRO   ascii
                mov     ah,2
                mov     dl,ascii
                int     21h
                ENDM
;—————————————————————————-
DSEG            SEGMENT USE16                 ;16位数据段
;—————————————————————————-
GDT             LABEL   BYTE                  ;全局描述符表
DUMMY           Desc    <>                    ;空描述符
Code            Desc    <0ffffh,,,ATCE,,>     ;代码段描述符
DataS           Desc    <0ffffh,0,11h,ATDW,,> ;源数据段描述符
DataD           Desc    <0ffffh,,,ATDW,,>     ;目标数据段描述符
;—————————————————————————-
GDTLen          =       $-GDT                 ;全局描述符表长度
VGDTR           PDesc   <GDTLen-1,>           ;伪描述符
;—————————————————————————-
Code_Sel        =       Code-GDT              ;代码段选择子
DataS_Sel       =       Datas-GDT             ;源数据段选择子
DataD_Sel       =       DataD-GDT             ;目标数据段选择子
;—————————————————————————-
BufLen          =       256                   ;缓冲区字节长度
Buffer          DB      BufLen DUP(0)         ;缓冲区
;—————————————————————————-
DSEG            ENDS                          ;数据段定义结束
;—————————————————————————-
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                     ;计算并设置目标数据段基址
                add     ax,OFFSET Buffer
                adc     dx,0
                mov     WORD PTR DataD.BaseL,ax
                mov     BYTE PTR DataD.BaseM,dl
                mov     BYTE PTR DataD.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,DataS_Sel
                mov     ds,ax                  ;加载源数据段描述符
                mov     ax,DataD_Sel
                mov     es,ax                  ;加载目标数据段描述符
                cld
                xor     si,si
                xor     di,di                  ;设置指针初值
                mov     cx,BufLen/4            ;设置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     si,OFFSET Buffer
                cld
                mov     bp,BufLen/16
NextLine:       mov     cx,16
NextCh:         lodsb
                push    ax
                shr     al,1
                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
                add     al,90h
                daa
                adc     al,40h
                daa
                ret
ToASCII         ENDP
;—————————————————————————-
CSEG            ENDS                           ;代码段定义结束
;—————————————————————————-
                END     Start

3.关于实例步骤的注释
在源程序的开头首先包含了文件“386SCD.INC”,在此包含文件中定义了保护模式程序设计要用到的一些结构、宏及常量。
下面对各实现步骤作些说明。
(1)切换到保护方式的准备工作
在从实模式切换到保护模式之前,必须作必要的准备。准备工作的内容根据实际而定。最起码的准备工作是建立合适的全局描述符表,
并使用GDTR指向该GDT。因为在切换到保护方式时,至少要把代码段的选择子装载到CS,所以GDT中至少含有代码段的描述符。
从本实例源程序可见,全局描述符表GDT仅有四个描述符:
第一个是空描述符;
第二个是代码段描述符;
第三个为源数据段;
第四个为目标数据段描述符。

本实例各描述符中的段界限是在定义时设置的,并且除伪描述符VGDTR中的界限按GDT的实际长度设置外,
各使用的存储段描述符的界限都规定为0FFFFH。另外,描述符中的段属性也根据所描述段的类型被预置,
各属性的定义在包含文件386SCD.INC中均有说明。从属性值可知,这三个段都是16位段。
由于在切换到保护方式后就要引用GDT,所以在切换到保护方式前必须装载GDTR。实例中使用如下指令装载GDTR:
    LGDT  QWORD PTR VGDTR
该指令的功能是把存储器中的伪描述符VGDTR装入到全局描述符表寄存器GDTR中。伪描述符VGDTR的结构如前所述结构类型PDESC所示,
低字是以字节位单位的全局描述符表段的界限,高双字为描述符表段的线性基地址(本实例不启用分页机制,
所以线性地址等同于物理地址)。本实例中未涉及到局部描述符表及中断描述符表,后面的文章将作详细说明。
(2)由实模式切换到保护模式
在做好准备后,从实模式切换到保护模式并不难。原则上只要把控制寄存器CR0中的PE位置1即可。本实例采用如下三条指令设置PE位:
    mov     eax,cr0
    or      eax,1
    mov     cr0,eax
实际情况要比这复杂些。执行上面的三条指令后,处理器转入保护模式,但CS中的内容还是实模式下代码段的段值,
而不是保护模式下代码段的选择子,所以在取指令之前得把代码段的选择子装入CS。为此,紧接着这三条指令,
安排一条如下所示的段间转移指令:
    JUMP16  Code_Sel,<OFFSET Virtual>
这条段间转移指令在实模式下被预取并在保护方式下被执行。利用这条段间转移指令可把保护模式下代码段的选择子装入CS,
同时也刷新指令预取队列。从此真正进入保护模式。
(3)由保护模式切换到实模式
在80386上,从保护模式切换到实模式的过程类似于从实模式切换到保护模式。原则上只要把控制寄存器CR0中的PE位清0即可。
实际上,在此之后也要安排一条段间转移指令,一方面清指令预取队列,另一方面把实模式下代码段的段值送CS。
这条段间转移指令在保护方式下被预取并在实模式下被执行。
(4)保护模式下的数据传送
首先,把源数据段和目标数据段的选择子装入DS和ES寄存器,这两个描述符已在实模式下设置好,
把选择子装入段寄存器就意味着把包括基地址在内的段信息装入到了段描述符高速缓冲寄存器。
然后设置指针寄存器SI和DI的初值,也设置计数器CX的初值。根据预置的段属性,在保护方式下,代码段也仅是16位段,
串操作指令只使用16位的SI、DI和CX等寄存器。最后利用串操作指令实施传送。
(5)显示缓冲区中的内容
由于缓冲区在常规内存中,所以在实模式下根据要求按十六进制显示其内容是很容易理解的,这里就不再多说。

4.内存映象

在源程序中没有把GDT作为一个单独的段对待,但在进入保护方式后,它是一个独立的段。从对代码段和源数据段描述符所赋的基地址和段界限值可见,代码段和数据段有部分覆盖。尽管这样做不利于代码和数据的安全,但如果需要,这样做是可行的。本实例运行时的内存映象如下图所示。


5.特别说明

作为第一个实模式和保护模式切换的例子,本实例作了大量的简化处理。
通常,由实模式切换到保护模式的准备工作还应包含建立中断描述符表。但本实例没有建立中断描述符表。为此,要求整个过程在关中断的情况下进行;要求不使用软中断指令;假设不发生任何异常。否则会导致系统崩溃。
本实例未使用局部描述符表,所以在进入保护模式后没有设置局部描述符表寄存器LDTR。为此,在保护模式下使用的段选择子都指定GDT中的描述符。
本实例未定义保护模式下的堆栈段,GDT中没有堆栈段描述符,在保护模式下没有设置SS,所以在保护方式下没有涉及堆栈操作的指令。
本实例各描述符特权级DPL和各选择子的请求特权级RPL均为0,在保护方式下运行时的当前特权级CPL也是0。
本实例没有采用分页管理机制,也即CR0中的PG位为0,线性地址就是存储单元的物理地址。

2004年12月30日

<!– 显示msn在线状态,注意:必须在msn中设置允许所有人查看你的状态才有效,否则msn图标永远发灰 –>
<A HREF=”http://snind.gotdns.com:8080/message/msn/hqy@sina.com.cn“>
<IMG SRC=”http://snind.gotdns.com:8080/msn/hqy@sina.com.cn
align=”absmiddle” border=”0″ ALT=”MSN Online Status Indicator”
onerror=”this.onerror=null;this.src=’http://snind.gotdns.com:8080/image/msnunknown.gif’;”>

<!– QQ在线状态显示 –>
<a target=blank href=http://wpa.qq.com/msgrd?V=1&Uin=692177&Exe=QQ&Site=Simplelife&Menu=yes><img border=”0″ SRC=http://wpa.qq.com/pa?p=1:692177:4 alt=”点击发送消息给我”></a>

<!– 时钟clock_035.swf 的035为时钟样式,可自己选择 –>
<EMBED SRC=http://www.butabon.com/Clock/clock_035.swf WIDTH=150 HEIGHT=150 wmode=transparent quality=high loop=true menu=false>
<!– 天气预报 注意这里涉及到编码问题,%B1%B1%BE%A9为城市代码,可用百度查一下,比如输入北京,地址栏中就会出现%B1%B1%BE%字样的内容–>
<center><iframe width=157 height=240 frameborder=0 scrolling=NO src=’http://appnews.qq.com/cgi-bin/news_qq_search?city=%BA%BC%D6%DD’></iframe></center>
<!– 空气质量 –>
<center><iframe width=157 height=240 frameborder=0 scrolling=NO src=’http://218.108.20.186:8080/kqzl/Kqzl_list_colindex.jsp’></iframe></center>