Code
; ==========================================
; pmtest2.asm
; 编译方法:nasm pmtest2.asm -o pmtest2.com
; ==========================================
%INCLUDE "pm.inc" ; 常量, 宏, 以及一些说明
ORG 0100h
JMP LABEL_BEGIN
[SECTION .gdt]
; GDT
; 段基址, 段界限 , 属性
LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ; Normal 描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32 ; 非一致代码段, 32
LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ; 非一致代码段, 16
LABEL_DESC_DATA: Descriptor 0, DataLen - 1, DA_DRW ; Data
LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA + DA_32 ; Stack, 32 位
LABEL_DESC_TEST: Descriptor 0500000h, 0ffffh, DA_DRW
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址
; GDT 结束
GdtLen EQU $ - LABEL_GDT ; GDT长度
GdtPtr DW GdtLen - 1 ; GDT界限
DD 0 ; GDT基地址
; GDT 选择子
SelectorNormal EQU LABEL_DESC_NORMAL - LABEL_GDT
SelectorCode32 EQU LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16 EQU LABEL_DESC_CODE16 - LABEL_GDT
SelectorData EQU LABEL_DESC_DATA - LABEL_GDT
SelectorStack EQU LABEL_DESC_STACK - LABEL_GDT
SelectorTest EQU LABEL_DESC_TEST - LABEL_GDT
SelectorVideo EQU LABEL_DESC_VIDEO - LABEL_GDT
; END of [SECTION .gdt]
[SECTION .data1] ; 数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode DW 0
; 字符串
PMMessage: DB "In Protect Mode now. ^-^", 0 ; 进入保护模式后显示此字符串
OffsetPMMessage EQU PMMessage - $$
StrTest: DB "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
OffsetStrTest EQU StrTest - $$
DataLen EQU $ - LABEL_DATA
; END of [SECTION .data1]
; 全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512 DB 0
TopOfStack EQU $ - LABEL_STACK - 1
; END of [SECTION .gs]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
MOV AX, CS
MOV DS, AX
MOV ES, AX
MOV SS, AX
MOV SP, 0100h
MOV [LABEL_GO_BACK_TO_REAL+3], AX ;因为LABEL_GO_BACK_TO_REAL后的代码是个mov语句,对应的字节有三个,第三个自己恰是跳转地址
MOV [SPValueInRealMode], SP
; 初始化 16 位代码段描述符
MOV AX, CS
movzx EAX, AX
SHL EAX, 4
ADD EAX, LABEL_SEG_CODE16
MOV WORD [LABEL_DESC_CODE16 + 2], AX
SHR EAX, 16
MOV BYTE [LABEL_DESC_CODE16 + 4], AL
MOV BYTE [LABEL_DESC_CODE16 + 7], AH
; 初始化 32 位代码段描述符
XOR EAX, EAX
MOV AX, CS
SHL EAX, 4
ADD EAX, LABEL_SEG_CODE32
MOV WORD [LABEL_DESC_CODE32 + 2], AX
SHR EAX, 16
MOV BYTE [LABEL_DESC_CODE32 + 4], AL
MOV BYTE [LABEL_DESC_CODE32 + 7], AH
; 初始化数据段描述符
XOR EAX, EAX
MOV AX, DS
SHL EAX, 4
ADD EAX, LABEL_DATA
MOV WORD [LABEL_DESC_DATA + 2], AX
SHR EAX, 16
MOV BYTE [LABEL_DESC_DATA + 4], AL
MOV BYTE [LABEL_DESC_DATA + 7], AH
; 初始化堆栈段描述符
XOR EAX, EAX
MOV AX, DS
SHL EAX, 4
ADD EAX, LABEL_STACK
MOV WORD [LABEL_DESC_STACK + 2], AX
SHR EAX, 16
MOV BYTE [LABEL_DESC_STACK + 4], AL
MOV BYTE [LABEL_DESC_STACK + 7], AH
; 为加载 GDTR 作准备
XOR EAX, EAX
MOV AX, DS
SHL EAX, 4
ADD EAX, LABEL_GDT ; eax <- gdt 基地址
MOV dword [GdtPtr + 2], EAX ; [GdtPtr + 2] <- gdt 基地址
; 加载 GDTR
lgdt [GdtPtr]
; 关中断
CLI
; 打开地址线A20
IN AL, 92h
OR AL, 00000010b
OUT 92h, AL
; 准备切换到保护模式
MOV EAX, cr0
OR EAX, 1
MOV cr0, EAX
; 真正进入保护模式
JMP dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0 处
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里
MOV AX, CS
MOV DS, AX
MOV ES, AX
MOV SS, AX
MOV SP, [SPValueInRealMode]
IN AL, 92h ; ┓
AND AL, 11111101b ; ┣ 关闭 A20 地址线
OUT 92h, AL ; ┛
STI ; 开中断
MOV AX, 4c00h ; ┓
INT 21h ; ┛回到 DOS
; END of [SECTION .s16]
[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS 32]
LABEL_SEG_CODE32:
MOV AX, SelectorData
MOV DS, AX ; 数据段选择子
MOV AX, SelectorTest
MOV ES, AX ; 测试段选择子
MOV AX, SelectorVideo
MOV gs, AX ; 视频段选择子
MOV AX, SelectorStack
MOV SS, AX ; 堆栈段选择子
MOV ESP, TopOfStack
; 下面显示一个字符串
MOV AH, 0Ch ; 0000: 黑底 1100: 红字
XOR ESI, ESI
XOR EDI, EDI
MOV ESI, OffsetPMMessage ; 源数据偏移
MOV EDI, (80 * 10 + 0) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
CLD
.1:
lodsb
TEST AL, AL
JZ .2
MOV [gs:EDI], AX
ADD EDI, 2
JMP .1
.2: ; 显示完毕
CALL DispReturn
CALL TestRead
CALL TestWrite
CALL TestRead
; 到此停止
JMP SelectorCode16:0
; ------------------------------------------------------------------------
TestRead:
XOR ESI, ESI
MOV ECX, 8
.loop
MOV AL, [ES:ESI]
CALL DispAL
INC ESI
LOOP .loop
CALL DispReturn
RET
; TestRead 结束-----------------------------------------------------------
; ------------------------------------------------------------------------
TestWrite:
PUSH ESI
PUSH EDI
XOR ESI, ESI
XOR EDI, EDI
MOV ESI, OffsetStrTest ; 源数据偏移
CLD
.1:
lodsb
TEST AL, AL
JZ .2
MOV [ES:EDI], AL
INC EDI
JMP .1
.2:
POP EDI
POP ESI
RET
; TestWrite 结束----------------------------------------------------------
; ------------------------------------------------------------------------
; 显示 AL 中的数字
; 默认地:
; 数字已经存在 AL 中
; edi 始终指向要显示的下一个字符的位置
; 被改变的寄存器:
; ax, edi
; ------------------------------------------------------------------------
DispAL:
PUSH ECX
PUSH EDX
MOV AH, 0Ch ; 0000: 黑底 1100: 红字
MOV DL, AL
SHR AL, 4
MOV ECX, 2
.begin:
AND AL, 01111b
CMP AL, 9
JA .1
ADD AL, '0'
JMP .2
.1:
SUB AL, 0Ah
ADD AL, 'A'
.2:
MOV [gs:EDI], AX
ADD EDI, 2
MOV AL, DL
LOOP .begin
ADD EDI, 2
POP EDX
POP ECX
RET
; DispAL 结束-------------------------------------------------------------
; ------------------------------------------------------------------------
DispReturn:
PUSH EAX
PUSH EBX
MOV EAX, EDI
MOV BL, 160
DIV BL
AND EAX, 0FFh
INC EAX
MOV BL, 160
MUL BL
MOV EDI, EAX
POP EBX
POP EAX
RET
; DispReturn 结束---------------------------------------------------------
SegCode32Len EQU $ - LABEL_SEG_CODE32
; END of [SECTION .s32]
; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
; 跳回实模式:
MOV AX, SelectorNormal
MOV DS, AX
MOV ES, AX
MOV fs, AX
MOV gs, AX
MOV SS, AX
MOV EAX, cr0
AND AL, 11111110b
MOV cr0, EAX
LABEL_GO_BACK_TO_REAL:
JMP 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值
Code16Len EQU $ - LABEL_SEG_CODE16
; END of [SECTION .s16code]
; ==========================================
; pmtest2.asm
; 编译方法:nasm pmtest2.asm -o pmtest2.com
; ==========================================
%INCLUDE "pm.inc" ; 常量, 宏, 以及一些说明
ORG 0100h
JMP LABEL_BEGIN
[SECTION .gdt]
; GDT
; 段基址, 段界限 , 属性
LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ; Normal 描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32 ; 非一致代码段, 32
LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ; 非一致代码段, 16
LABEL_DESC_DATA: Descriptor 0, DataLen - 1, DA_DRW ; Data
LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA + DA_32 ; Stack, 32 位
LABEL_DESC_TEST: Descriptor 0500000h, 0ffffh, DA_DRW
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址
; GDT 结束
GdtLen EQU $ - LABEL_GDT ; GDT长度
GdtPtr DW GdtLen - 1 ; GDT界限
DD 0 ; GDT基地址
; GDT 选择子
SelectorNormal EQU LABEL_DESC_NORMAL - LABEL_GDT
SelectorCode32 EQU LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16 EQU LABEL_DESC_CODE16 - LABEL_GDT
SelectorData EQU LABEL_DESC_DATA - LABEL_GDT
SelectorStack EQU LABEL_DESC_STACK - LABEL_GDT
SelectorTest EQU LABEL_DESC_TEST - LABEL_GDT
SelectorVideo EQU LABEL_DESC_VIDEO - LABEL_GDT
; END of [SECTION .gdt]
[SECTION .data1] ; 数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode DW 0
; 字符串
PMMessage: DB "In Protect Mode now. ^-^", 0 ; 进入保护模式后显示此字符串
OffsetPMMessage EQU PMMessage - $$
StrTest: DB "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
OffsetStrTest EQU StrTest - $$
DataLen EQU $ - LABEL_DATA
; END of [SECTION .data1]
; 全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512 DB 0
TopOfStack EQU $ - LABEL_STACK - 1
; END of [SECTION .gs]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
MOV AX, CS
MOV DS, AX
MOV ES, AX
MOV SS, AX
MOV SP, 0100h
MOV [LABEL_GO_BACK_TO_REAL+3], AX ;因为LABEL_GO_BACK_TO_REAL后的代码是个mov语句,对应的字节有三个,第三个自己恰是跳转地址
MOV [SPValueInRealMode], SP
; 初始化 16 位代码段描述符
MOV AX, CS
movzx EAX, AX
SHL EAX, 4
ADD EAX, LABEL_SEG_CODE16
MOV WORD [LABEL_DESC_CODE16 + 2], AX
SHR EAX, 16
MOV BYTE [LABEL_DESC_CODE16 + 4], AL
MOV BYTE [LABEL_DESC_CODE16 + 7], AH
; 初始化 32 位代码段描述符
XOR EAX, EAX
MOV AX, CS
SHL EAX, 4
ADD EAX, LABEL_SEG_CODE32
MOV WORD [LABEL_DESC_CODE32 + 2], AX
SHR EAX, 16
MOV BYTE [LABEL_DESC_CODE32 + 4], AL
MOV BYTE [LABEL_DESC_CODE32 + 7], AH
; 初始化数据段描述符
XOR EAX, EAX
MOV AX, DS
SHL EAX, 4
ADD EAX, LABEL_DATA
MOV WORD [LABEL_DESC_DATA + 2], AX
SHR EAX, 16
MOV BYTE [LABEL_DESC_DATA + 4], AL
MOV BYTE [LABEL_DESC_DATA + 7], AH
; 初始化堆栈段描述符
XOR EAX, EAX
MOV AX, DS
SHL EAX, 4
ADD EAX, LABEL_STACK
MOV WORD [LABEL_DESC_STACK + 2], AX
SHR EAX, 16
MOV BYTE [LABEL_DESC_STACK + 4], AL
MOV BYTE [LABEL_DESC_STACK + 7], AH
; 为加载 GDTR 作准备
XOR EAX, EAX
MOV AX, DS
SHL EAX, 4
ADD EAX, LABEL_GDT ; eax <- gdt 基地址
MOV dword [GdtPtr + 2], EAX ; [GdtPtr + 2] <- gdt 基地址
; 加载 GDTR
lgdt [GdtPtr]
; 关中断
CLI
; 打开地址线A20
IN AL, 92h
OR AL, 00000010b
OUT 92h, AL
; 准备切换到保护模式
MOV EAX, cr0
OR EAX, 1
MOV cr0, EAX
; 真正进入保护模式
JMP dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0 处
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里
MOV AX, CS
MOV DS, AX
MOV ES, AX
MOV SS, AX
MOV SP, [SPValueInRealMode]
IN AL, 92h ; ┓
AND AL, 11111101b ; ┣ 关闭 A20 地址线
OUT 92h, AL ; ┛
STI ; 开中断
MOV AX, 4c00h ; ┓
INT 21h ; ┛回到 DOS
; END of [SECTION .s16]
[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS 32]
LABEL_SEG_CODE32:
MOV AX, SelectorData
MOV DS, AX ; 数据段选择子
MOV AX, SelectorTest
MOV ES, AX ; 测试段选择子
MOV AX, SelectorVideo
MOV gs, AX ; 视频段选择子
MOV AX, SelectorStack
MOV SS, AX ; 堆栈段选择子
MOV ESP, TopOfStack
; 下面显示一个字符串
MOV AH, 0Ch ; 0000: 黑底 1100: 红字
XOR ESI, ESI
XOR EDI, EDI
MOV ESI, OffsetPMMessage ; 源数据偏移
MOV EDI, (80 * 10 + 0) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
CLD
.1:
lodsb
TEST AL, AL
JZ .2
MOV [gs:EDI], AX
ADD EDI, 2
JMP .1
.2: ; 显示完毕
CALL DispReturn
CALL TestRead
CALL TestWrite
CALL TestRead
; 到此停止
JMP SelectorCode16:0
; ------------------------------------------------------------------------
TestRead:
XOR ESI, ESI
MOV ECX, 8
.loop
MOV AL, [ES:ESI]
CALL DispAL
INC ESI
LOOP .loop
CALL DispReturn
RET
; TestRead 结束-----------------------------------------------------------
; ------------------------------------------------------------------------
TestWrite:
PUSH ESI
PUSH EDI
XOR ESI, ESI
XOR EDI, EDI
MOV ESI, OffsetStrTest ; 源数据偏移
CLD
.1:
lodsb
TEST AL, AL
JZ .2
MOV [ES:EDI], AL
INC EDI
JMP .1
.2:
POP EDI
POP ESI
RET
; TestWrite 结束----------------------------------------------------------
; ------------------------------------------------------------------------
; 显示 AL 中的数字
; 默认地:
; 数字已经存在 AL 中
; edi 始终指向要显示的下一个字符的位置
; 被改变的寄存器:
; ax, edi
; ------------------------------------------------------------------------
DispAL:
PUSH ECX
PUSH EDX
MOV AH, 0Ch ; 0000: 黑底 1100: 红字
MOV DL, AL
SHR AL, 4
MOV ECX, 2
.begin:
AND AL, 01111b
CMP AL, 9
JA .1
ADD AL, '0'
JMP .2
.1:
SUB AL, 0Ah
ADD AL, 'A'
.2:
MOV [gs:EDI], AX
ADD EDI, 2
MOV AL, DL
LOOP .begin
ADD EDI, 2
POP EDX
POP ECX
RET
; DispAL 结束-------------------------------------------------------------
; ------------------------------------------------------------------------
DispReturn:
PUSH EAX
PUSH EBX
MOV EAX, EDI
MOV BL, 160
DIV BL
AND EAX, 0FFh
INC EAX
MOV BL, 160
MUL BL
MOV EDI, EAX
POP EBX
POP EAX
RET
; DispReturn 结束---------------------------------------------------------
SegCode32Len EQU $ - LABEL_SEG_CODE32
; END of [SECTION .s32]
; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
; 跳回实模式:
MOV AX, SelectorNormal
MOV DS, AX
MOV ES, AX
MOV fs, AX
MOV gs, AX
MOV SS, AX
MOV EAX, cr0
AND AL, 11111110b
MOV cr0, EAX
LABEL_GO_BACK_TO_REAL:
JMP 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值
Code16Len EQU $ - LABEL_SEG_CODE16
; END of [SECTION .s16code]