这个课程讲的是怎么由实模式进入保护模式,正如文章所说,学完之后完全是云里雾里。
简单记录一下自己所学,希望继续学习下去能够柳暗花明吧。
课程链接:
网易课程:java开发操作系统内核:由实模式进入保护模式之32位寻址
CSDN博客:java开发操作系统内核:由实模式进入保护模式之32位寻址
建议这个课程一定要看网易的视频,只看文章完全看不懂。
1. 知识储备
2. 代码
boot.asm
org 0x7c00;
LOAD_ADDR EQU 0X8000
entry:
mov ax, 0
mov ss, ax
mov ds, ax
mov es, ax
mov si, ax
readFloppy:
mov CH, 0 ;CH 用来存储柱面号(读取位置要与java代码写入img文件时写入的位置一致)
mov DH, 0 ;DH 用来存储磁头号
mov CL, 2 ;CL 用来存储扇区号
mov BX, LOAD_ADDR ; ES:BX 数据存储缓冲区
mov AH, 0x02 ; AH = 02 表示要做的是读盘操作
mov AL, 1 ; AL 表示要连续读取几个扇区
mov DL, 0 ;驱动器编号,一般我们只有一个软盘驱动器,所以写死
;为0
INT 0x13 ;调用BIOS中断实现磁盘读取功能
JC fin
jmp LOAD_ADDR
fin:
HLT
jmp fin
kernel.asm
%macro Descriptor 3
dw %2 & 0FFFFh
dw %1 & 0FFFFh
db (%1>>16) & 0FFh
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)
db (%1 >> 24) & 0FFh
%endmacro
DA_32 EQU 4000h ; 32 位段//不太明白这个32位段表示啥意思?
DA_C EQU 98h ; 存在的只执行代码段属性值
DA_DRW EQU 92h ; 存在的可读写数据段属性值
org 0x9000
;//为啥要直接跳转到LABEL_BEGIN呢?下面的定义GDT相关的代码不用执行吗
jmp LABEL_BEGIN
[SECTION .gdt]
;利用上面定义的宏来定义结构体,和下面定义GdtPtr的方式一样
; 段基址 段界限 属性
LABEL_GDT: Descriptor 0, 0, 0
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW
;$表示当前地址
GdtLen equ $ - LABEL_GDT
GdtPtr dw GdtLen - 1
dd 0
;计算段选项符。其实就是对应的段在GDT表中的偏移
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
;现在的cs寄存器是0吗?那是不是可以直接给ax赋0?
;改成0试了一下确实可以正常运行.
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
;这一段代码表示的是实模式下的寻址:cs*16+偏移地址
;重复cpu在实模式下的寻址计算方法,得到当前段的地址,再通过偏移得到LABEL_SEG_CODE32的地址。
;将要运行的代码段LABEL_SEG_CODE32的地址写入GDT表中
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
;创建GdtPtr
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT
mov dword [GdtPtr + 2], eax
;保存GDP表的地址和长度到寄存器中:寄存器GDTR用来存放GDT的入口地址,以及长度
lgdt [GdtPtr]
cli ;关中断
in al, 92h
or al, 00000010b
out 92h, al
mov eax, cr0
or eax , 1
mov cr0, eax
;跳转,通过SelectorCode32到GDT表中查找代码的位置
;现在已经进入保护模式,寻址方式也已经发生了改变。
jmp dword SelectorCode32: 0
[SECTION .s32]
[BITS 32]
LABEL_SEG_CODE32:
mov ax, SelectorVideo
mov gs, ax
mov si, msg
mov ebx, 10
mov ecx, 2
showChar:
mov edi, (80*11)
add edi, ebx
mov eax, edi
mul ecx
mov edi, eax
mov ah, 0ch
mov al, [si]
cmp al, 0
je end
add ebx,1
add si, 1
mov [gs:edi], ax
jmp showChar
end:
jmp $
msg:
DB "Protect Mode, write by copbint!", 0
SegCode32Len equ $ - LABEL_SEG_CODE32
3. 效果
1)先分别编译:
nasm -o kernel.bat kernel.asm
nasm -o boot.bat boot.asm
2)再运行作者提供的java程序,制作img文件。
3)设置虚拟机从img文件启动
4. 疑问
1) 讲实模式到保护模式的切换,为什么要引入GDT表呢?
这两者似乎没有必然的关系啊?不理解。。。
20190830补充:
答:
保护模式下必须使用分段的寻址方式,GDT表是分段寻址的必要前提,所以必须要构造GDT表。
5. 保护模式下操作5M地址内存
后面紧接着有一个课程是如何向5M地址空间的内存写入数据并读出,展示的是保护模式的寻址能力,相比于本篇文章,没啥新东西。故略过。
保护模式超强的寻址功能:天空任鸟飞