📄 mode_t.asm
字号:
.MODEL HUGE
STACK SEGMENT STACK PARA 'STACK';设堆栈
DB 200 DUP(0)
STACK ENDS
include g_def.asm ;定义描述符结构和选择符
CODE SEGMENT WORD PUBLIC 'CODE'
ASSUME CS:CODE,DS:DATA,SS:STACK
MODE_CHANGE PROC FAR
START: MOV AX,DATA ;实模式
MOV DS,AX ;DS=DATA
CLI ;关中断
MOV AX,STACK
MOV SS,AX ;SS=STACK
MOV SAVE_SS,AX ;备份SS
MOV SP,200
MOV SAVE_SP,SP ;备份SP
STI ;关中断
MOV DS:FILE_OVER,1 ;文件没有读完标志位
;
.386P ;启用80386保护方式下的指令
MOV AH,0DFH
CALL OPEN_A20 ;打开地址线A20,函数在程序文件A20.ASM中
; 保存初始(BIOS) GDTR 与 IDTR ,实模式用
SGDT FWORD PTR SAVE_GDT ;备份GDT
SIDT FWORD PTR SAVE_IDT ;备份IDT
;设置保护模式下要使用的与CS、DS、SS、GDT BUF相应描述符
MOV DI,OFFSET GDT_CS
MOV AX,CS
MOV CX,10H
MUL CX ;AX*16即左移4位,低位在AX,高位在DX
MOV [DI].BASE_L,AX ;GDT_CS.BASE_L = CS×16
MOV [DI].BASE_M,DL
;set desc for ds 等于初始化GDT,以下给在保护模式下的CS,DS,SS,GDT_BUF的基址和内存段设值
MOV DI,OFFSET GDT_DS ;AX*16即左移4位,低位在AX,高位在DX
MOV DX,0
MOV AX,DS
MOV CX,10H
MUL CX
MOV [DI].BASE_L,AX ;GDT_DS.BASE_L=DS×16 ___________________________________
MOV [DI].BASE_M,DL ;GDT_DS.BASE_M=0 |BASE_H | BASE_M | BASE_L |
MOV [DI].BASE_H,DH ;GDT_DS.BASE_H=0 | 0 | 0 | DS×16 |
;set desc for ss
MOV DI,OFFSET GDT_SS ;AX*16即左移4位,低位在AX,高位在DX
MOV AX,SS
MOV CX,10H
MUL CX
MOV [DI].BASE_L,AX ;GDT_SS.BASE_L=SS×16
MOV [DI].BASE_M,DL ;GDT_SS.BASE_M=0
;set desc for gdt_buf
MOV DI,OFFSET GDT_BUF
;设置GDT_BUF,基址=2M,限长=1M
MOV [DI].BASE_L,0 ;GDT_BUF.BASE_L=0
MOV [DI].BASE_M,020H ;GDT_SS.BASE_M=020H,200000H为2MB
MOV [DI].GRAN,0FH ;缓冲区最大1M
;计算并保存GDT表的基地址(DX:AX)和大小
CLI
MOV AX,DS ;AX*16即左移4位,低位在AX,高位在DX
MOV CX,10H
MUL CX ;AX=DS×16+GDT_DEF
ADD AX,OFFSET GDT_DEF ;DX:AX保存GDT表的32位地址(实模式下的实际物理地址(段*16+偏移
ADC DX,0
MOV DTLOAD,AX ;DTLOAD=GDT_DEF的地址
MOV DTLOAD+2,DX
MOV AX,OFFSET GDT_SIZE ;将GDT_SIZE的偏移地址赋给AX
SUB AX,OFFSET DTSIZE ;DTSIZE=(GDT_SIZE的偏移地址-DISIZE的偏移地址+1)
ADD AX,1
MOV DTSIZE,AX
;***********************************************
; mov file to c_buffer,打开文件读,句柄放在FILE_HANDLE
MOV DS:G_BUF_SUM,0
MOV AX,3D02H ;AH=3DH表示读取文件,AL=02H表示为读写打开文件
LEA DX,FILE_NAME ;DX存放文件名串地址
INT 21H
;以上4行代码获得文件的句柄
JNC READ_FILE0 ;CX=1表示打开文件出错
LEA DX,ERR_FILE
MOV AH,9
INT 21H
;以上4行代码显示错误信息
MOV AH,4CH
INT 21H ;未发现文件,退出,即带返回码结束
READ_FILE0: MOV DS:FILE_HANDLE,AX ;保存文件句柄,开始读文件
READ_FILE:
MOV DS:SAVE_SS,SS
MOV DS:SAVE_SP,SP
SGDT FWORD PTR SAVE_GDT ;保存GDTR & IDTR
SIDT FWORD PTR SAVE_IDT
MOV BX,DS:FILE_HANDLE ;BX放文件句柄
MOV CX,1024 ;CX存放读取字节
MOV DX,OFFSET C_BUFFER ;DX存放数据缓冲区的偏移地址
MOV AH,3FH ;AH=3FH表示读取文件
INT 21H ;调用DOS 21H中断
;以上5行代码从文件中读1024字节到C_BUFFER中
JNC READ_FILE1 ;CX=1表示读取文件出错,其实是剩下字节不够1024
MOV AX,0
MOV DS:C_BUF_SIZE,AX
MOV DS:FILE_OVER,AX
MOV AH,3EH ;关闭文件
INT 21H
JMP PROTECT_MODE ;跳到保护模式
READ_FILE1: MOV DS:C_BUF_SIZE,AX ;不够1024字节,读取剩下的字节后关闭文件
CMP AX,1024 ;等于1024字节,则跳转到PROTECT_MODE
JNL PROTECT_MODE
MOV DS:FILE_OVER,0 ;读文件结束, 将文件结束标志设置为0
MOV BX,DS:FILE_HANDLE
MOV AH,3EH ;close file
INT 21H
;*********************************************
PROTECT_MODE: ;切换到保护模式
;用LGDT指令把GDT表的基地址和边界值加载到GDTR,使GDT有效
cli
LGDT FWORD PTR DTSIZE ;加载GDTR
MOV AX,GDT_DS_SEL ;加载数据段描述符
MOV DS,AX
MOV ES,AX
MOV FS,AX
MOV GS,AX
;CR0的PE位置1,允许保护模式,并用JMP指令清指令队列中预取到的实模式代码,再用段间跳转指令进入保护模式。
sti
;
MOV EAX,00000001H ;CR0的PE位置1,允许保护模式
MOV CR0,EAX
JMP EDS_FLUSH ;清指令预取队列,并真正进入保护模式
EDS_FLUSH: DB 0EAH ;0EAH其实是短跳转指令,南航汇编书后有介绍
DW OFFSET MOVE_BUF ;跳转的偏移地址
DW GDT_CS_SEL ;跳转的段地址
;****************************************
MOVE_BUF: ;保护模式
MOV AX,GDT_DS_SEL
MOV FS,AX
MOV AX,GDT_BUF_SEL
MOV GS,AX
CMP FS:C_BUF_SIZE,1 ;C_BUF_SIZE<1 则->VRAM_DISP,显示文件
JL VRAM_DISP ;文件已经读完
MOV ECX,0
MOV CX,FS:C_BUF_SIZE
MOV EAX,OFFSET C_BUFFER
MOV ESI,EAX
MOV EDI,FS:G_BUF_SUM
MOVE_BUF1: MOV AL,FS:[ESI] ;move to 2M 移动数据到扩展内存
MOV GS:[EDI],AL ;GDT_BUF_SEL:[EDI]=GDT_DS_SEL:[ESI]
INC EDI ;将C_BUFFER的数据移至扩展内存G_BUF_SUM开始后的地方
INC ESI
LOOP MOVE_BUF1
MOV FS:G_BUF_SUM,EDI ;G_BUF_SUM=数据大小
CMP FS:FILE_OVER,1
JL VRAM_DISP ;FILE_OVER<1 则去显示
;***************** ;若文件未读完回实模式去读文件
CLI
MOV EAX,CR0
AND AX,NOT PE_ON
MOV CR0,EAX ;置CR0为实模式
;
DB 0EAH ;同样为跳转指令
DW OFFSET REAL_MODE0
DW CODE
REAL_MODE0: MOV AH,0DDH ;进实模式
CALL CLOSE_A20 ;关闭A20地址线
;
MOV AX,DATA
MOV DS,AX ;置实模式DS、SS、SP的值
LGDT FWORD PTR SAVE_GDT ;恢复GDTR、IDTR的内容
LIDT FWORD PTR SAVE_IDT
cli
MOV SS,SAVE_SS
MOV SP,SAVE_SP
sti
JMP READ_FILE ;重新读取文件
;***************************************
; all of file are moved to g_buf
VRAM_DISP: ;在保护模式下访问显示缓存(VRAM),在屏幕上显示文本
;文件已经读完时,将扩展内存中数据写到显示缓存 B8000H
MOV AX,GDT_DS_SEL ;将各个选择子置入段中
MOV FS,AX
MOV AX,GDT_BUF_SEL
MOV GS,AX
MOV AX,VRAM_SEL
MOV ES,AX
MOV CX,08D0H
MOV BX,0
;下面3行代码不停地将VRAM_SEL中的内容全赋为0720H
L1: MOV ES:[BX],0720H ;高字节存放显示属性,低字节放显示内容
ADD BX,2
LOOP L1
MOV ECX,FS:G_BUF_SUM
CMP ECX,2048 ;ECX<2048?时则至LL,否则将ECX赋值为2048
JL LL
MOV ECX,2048 ;因为2048/2=1024
LL: MOV ESI,0 ;ECX<2048
MOV EBX,480
L2: MOV AL,GS:[ESI] ;将GDT_BUF_SEL中的内容转移至VRAM_SEL初始:VRAM_SEL:[480]=GDT_BUF_SEL:[0]
MOV AH,22
MOV ES:[EBX],AX ;显存地址为0B000:0000,低字节放显示内容,高字节放显示属性
ADD EBX,2 ;AH=22表示显示属性,显示属性共8个字节
INC ESI ;7为闪烁标志(1闪)654为背景(000黑,111白)3为亮度(0正常,1加亮),210为前景
LOOP L2
;turn to real_mode
CLI
MOV EAX,CR0
AND AX,NOT PE_ON
MOV CR0,EAX
;
DB 0EAH
DW OFFSET REAL_MODE
DW CODE
REAL_MODE: MOV AH,0DDH
TO_DOS
.8086;只承认8086指令
MODE_CHANGE ENDP
include a20.asm
CODE ENDS
;数据段开始
DATA SEGMENT WORD PUBLIC 'DATA'
FILE_NAME DB '\TC\G\MODE.TXT',0
FILE_HANDLE DW 0
FILE_OVER DW 0
ERR_FILE DB 0DH,0AH,'Open file error$'
OK_MSG DB 0DH,0AH,'Have a nice day,Bye! $'
SAVE_GDT DW 4 DUP(0)
SAVE_IDT DW 4 DUP(0)
SAVE_SS DW 0
SAVE_SP DW 0
;定义GDT表开始
DTSIZE DW 0
DTLOAD DW 0,0
GDT_DEF EQU $
GDT_DESC <0,0,0,0,0,0>
GDT_CS GDT_DESC <0FFFFH,0,0,9AH,0,0>
;9AH=exec/read ,DPL=0
GDT_DS GDT_DESC <0FFFFH,0,0,92H,0,0>
;92H=P ,DPL=0,TYPE=2
GDT_SS GDT_DESC <0,0,0,96H,0,0>
;96H=R/W ,DOWN
GDT_VRM GDT_DESC <0FFFFH,08000H,0BH,92H,0,0>
;92H=P ,DPL=0,TYPE=2
GDT_BUF GDT_DESC <0FFFFH,0,0,92H,0,0>
;92H=P ,DPL=0,TYPE=2 ,length=1M
GDT_SIZE DW 0
;定义GDT表结束
C_BUF_SIZE DW 0
G_BUF_SUM DD 0
C_BUFFER DB 1024 DUP(?)
DATA ENDS
END START
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -