MacOS环境-手写操作系统-04-实模式进入保护模式 原创
实模式进入保护模式
文章写于两年前的 MacBookAir(2015)
目前笔者为 MacBookPro M1 (抽查了部分 都运行正常)
Github项目地址: https://github.com/wdkang123/MyOperatingSystem
MacOS X86架构(x新版的arm架构的我没有 所以大家自行测试)
VirtualBox
C/C++环境 (Xcode必装)
1.简介
从实模式到保护模式
保护模式 最重要的就是“保护”两字
有了“保护”功能后 CPU为软件提供了很多的功能 当然也有了很多的限制
但是“保护”并不是几行字能描述清楚的 所以先把代码跑起来
2.汇编
%include "pm.inc"
org 0x9000
jmp LABEL_BEGIN
[SECTION .gdt]
; 段基址 段界限 属性
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
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
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_GDT
mov dword [GdtPtr + 2], eax
lgdt [GdtPtr]
cli ;关中断
in al, 92h
or al, 00000010b
out 92h, al
mov eax, cr0
or eax , 1
mov cr0, eax
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", 0
SegCode32Len equ $ - LABEL_SEG_CODE32
再看看 pm.inc 的内容
%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 位段
DA_C EQU 98h ; 存在的只执行代码段属性值
DA_DRW EQU 92h ; 存在的可读写数据段属性值
对上面的代码进行编译
nasm -o kernel.bat kernel.asm
从LABEL_SEG_CODE32: 这一部分开始 代码就执行在保护模式下 这段代码的作用就是显示一串字符 gs是计算机的一个寄存器 它跟eax,ebx这些寄存器差不多 但作用更为单一 主要用来指向显存 当我们将信息写入gs指向的内存后 信息会显示到屏幕上
用于显示字符的显存,内存地址从0XB800h开始,从该地址开始,每两个字节用来在屏幕上显示一个字符 这两个字节中 第一个字节的信息用来表示字符的颜色 第二个字节用来存储要显示的字符的ASCII值 屏幕一行能显示80个字符 大家看到代码中有语句
mov edi, (80*11)
这表明我们要从第11行开始显示字符,接下来又有语句:
add edi, ebx
其中,ebx的值是11,这表明我们要从第11行的第10列开始显示字符串,接下来的语句是:
mov eax, edi
mul ecx
ecx的值是2 这个2就是我们前面说过的显示一个字符需要两个字节
上面几句汇编语句的作用是:
eax = ((80*11) + 10) * 2
这样eax就指向了第11行第11列所在的显存位置
接下来语句:
mov ah, 0ch
它的作用是在用来显示字符的两字节中,对第一个字节放入数值0ch,也就是设置字符的颜色,接下来的语句:
mov al, [si]
将寄存器si指向的字符的ascii值写入到第二个字节,这样,字符就显示到屏幕上了。
大家注意寄存器si的用法:[si]. si相当于C语言中的一个指针,指向内存某个地址,[si]就是读取si指向的内存地址的信息,等同于 c语言中的*(si)
3.保护模式显著特点
1.寻址空间从时模式的1M增强到4G
2.不同的代码拥有不同的优先级,优先级高的能够执行特殊指令,优先级低的,某些重要指令就无法执行。
要想进入保护模式 我们需要解决两个问题:
(1)如何获取超过1M以上的内存地址
(2)如何设置不同代码所具有的优先级
我们先看看寻找能力的变化,在实模式下,cpu是16位的,寄存器16位,数据总线16位,地址总线20位
于是寻找的范围必然受限于20位的地址总线,所以寻找范围无法超过1M(2^20).
要想实现4GB的寻址 我们必须使用32位来表示地址 intel是这么解决这个问题的,他们用连续的8个字节组成的结构体来解决一系列问题:
byte0
byte1
……
byte7
其中,字节2,3,4以及字节7,这四个字节合在一起总共有32位,这就形成了一个32位的地址。
同时把字节0,字节1,以及将字节6的拆成两部分,各4个bits,前4个bits跟字节0,字节1合在一起,形成一个20个bit的数据,用来表示要访问的内存长度
这样,我们就解决了内存寻址的问题
4.pm.inc
pm.inc里面的宏定义就是我们说的7字节数据结构
%macro Descriptor 3
表示要初始化该数据结构 需要传入3个参数 :
(1) %1表示引用第一个参数
(2) %2表示引用第二个参数
初始化该结构时,输入的一个参数是内存的地址,大家看语句:
dw %1 & 0FFFFh
db (%1>>16) & 0FFh
这两句就是把内存地址的头三个字节放入到byte2,byte3,byte4,最后一句:
db (%1 >> 24) & 0FFh
就是讲地址的第4个字节放入到byte7. 初始化数据结构的第二个参数表示的是要访问的内存的长度,大家看语句:
dw %2 & 0FFFFh
就是把内存长度的头两个字节写入byte0,byte1,语句:
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)
中的((%2 >> 8) & 0F00h)就是把内存长度的第16-19bit写入到byte6的前4个bit
由此要访问的内存和内存的长度就都设置好了
5.编译运行
将第2.汇编的代码进行编译
nasm boot.asm -o boot.bat
nasm kernel.asm -o kernel.bat
打开 java 工程 生成 system.img
最后 装载运行
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)