【asm】:几个重要程序——汇编语言
【asm】:几个重要程序——汇编语言
一、基础说明
1、【目录】:几个重要程序
----【CPU的模式切换:实模式(16位)——>保护模式(32位)】
----【CPU的模式切换三级跳:实模式(16位)——>保护模式(32位)——>长模式(64位)】(备注:这个程序无法通过编译,原因是程序中包含内存越界访问)
二、几个重要的程序
1、 CPU模式切换:实模式——>保护模式
[wit@ubuntu:src]$ cat boot.asm
[BITS 16]
[ORG 0x7C00]
start:
cli ; 关中断
lgdt [gdt_desc] ; 加载 GDT
; 切换到保护模式
mov eax, cr0
or eax, 1 ; 设置 PE 位
mov cr0, eax
; 远跳转刷新流水线
jmp 0x08:code32
[BITS 32]
code32:
mov ax, 0x10 ; 设置数据段
mov ds, ax
mov ss, ax
mov esp, 0x90000 ; 设置栈顶
; 此时可以检查 CR0 确认状态
mov eax, cr0 ; eax 中现在包含 CR0 的值,PE 位应为 1
; 死循环,防止跑飞
hlt
jmp $
; GDT 定义
gdt_start:
dq 0x0000000000000000 ; 空描述符
gdt_code:
dw 0xFFFF ; Limit (low)
dw 0x0000 ; Base (low)
db 0x00 ; Base (mid)
db 10011010b ; Access byte (Present, Ring 0, Code, Executable)
db 11001111b ; Flags + Limit (high)
db 0x00 ; Base (high)
gdt_data:
dw 0xFFFF
dw 0x0000
db 0x00
db 10010010b ; Access byte (Present, Ring 0, Data, Writable)
db 11001111b
db 0x00
gdt_end:
gdt_desc:
dw gdt_end - gdt_start - 1 ; GDT 界限
dd gdt_start ; GDT 基地址
times 510-($-$$) db 0
dw 0xAA55 ; 引导签名
2、 内存管理:GDT和段选择子(CPU从实模式到保护模式切换的关键)
[wit@ubuntu:src]$ cat boot.asm
[BITS 16]
[ORG 0x7C00]
start:
cli ; 关中断
lgdt [gdt_desc] ; 加载 GDT
; 切换到保护模式
mov eax, cr0
or eax, 1 ; 设置 PE 位
mov cr0, eax
; 远跳转刷新流水线
jmp 0x08:code32
[BITS 32]
code32:
mov ax, 0x10 ; 设置数据段
mov ds, ax
mov ss, ax
mov esp, 0x90000 ; 设置栈顶
; 此时可以检查 CR0 确认状态
mov eax, cr0 ; eax 中现在包含 CR0 的值,PE 位应为 1
; 死循环,防止跑飞
hlt
jmp $
; GDT 定义
gdt_start:
dq 0x0000000000000000 ; 空描述符
gdt_code:
dw 0xFFFF ; Limit (low)
dw 0x0000 ; Base (low)
db 0x00 ; Base (mid)
db 10011010b ; Access byte (Present, Ring 0, Code, Executable)
db 11001111b ; Flags + Limit (high)
db 0x00 ; Base (high)
gdt_data:
dw 0xFFFF
dw 0x0000
db 0x00
db 10010010b ; Access byte (Present, Ring 0, Data, Writable)
db 11001111b
db 0x00
gdt_end:
gdt_desc:
dw gdt_end - gdt_start - 1 ; GDT 界限
dd gdt_start ; GDT 基地址
times 510-($-$$) db 0
dw 0xAA55 ; 引导签名
3、 CPU模式切换三级跳:实模式16——>保护模式32——>长模式64
[wit@ubuntu:src]$ cat cpu_mode_switch.asm
; mode_switch.asm
; CPU模式三级跳:16位实模式 → 32位保护模式 → 64位长模式
; 编译:nasm -f bin -o mode_switch.bin mode_switch.asm
[BITS 16] ; 起步:16位实模式
[ORG 0x7C00] ; BIOS加载到0x7C00
;------------------------ 第一阶段:实模式准备 ------------------------
start:
cli ; 关中断,切换过程中不能被打扰
mov ax, 0x0000
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00 ; 设置实模式栈
; 1. 启用A20地址线(解锁1MB以上内存)
in al, 0x92
or al, 2
out 0x92, al
; 2. 加载GDT
lgdt [GDT_desc]
; 3. 置CR0的PE位,进入保护模式
mov eax, cr0
or al, 1
mov cr0, eax
; 4. 远跳转刷新指令流水线,正式进入32位保护模式
jmp dword 0x08:start_protected
;------------------------ GDT:全局描述符表 ------------------------
; 必须同时包含32位和64位的代码段/数据段描述符
align 8
GDT_start:
; 空描述符
GDT_null: dq 0x0000000000000000
; 32位代码段 (选择子 0x08)
GDT_code32: dw 0xFFFF ; 段限长[15:0]
dw 0x0000 ; 基址[15:0]
db 0x00 ; 基址[23:16]
db 10011010b ; P=1 DPL=00 S=1 TYPE=1010(可执行可读)
db 11001111b ; G=1 D=1(32位) 限长[19:16]
db 0x00 ; 基址[31:24]
; 32位数据段 (选择子 0x10)
GDT_data32: dw 0xFFFF
dw 0x0000
db 0x00
db 10010010b ; TYPE=0010(可读写)
db 11001111b
db 0x00
; 64位代码段 (选择子 0x18) - 长模式专用
GDT_code64: dw 0xFFFF
dw 0x0000
db 0x00
db 10011010b
db 10101111b ; L=1(64位) D=0
db 0x00
; 64位数据段 (选择子 0x20)
GDT_data64: dw 0xFFFF
dw 0x0000
db 0x00
db 10010010b
db 10101111b ; L=1 D=0
db 0x00
GDT_end:
GDT_desc: dw GDT_end - GDT_start - 1
dd GDT_start
;------------------------ 第二阶段:32位保护模式 ------------------------
[BITS 32]
start_protected:
; 更新段寄存器
mov ax, 0x10 ; 32位数据段选择子
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 0x90000 ; 设置32位栈顶
; 保护模式下可以做一些初始化工作
; 比如打印一条消息确认已进入保护模式
; (这里省略具体打印,我们直接准备跳长模式)
; --- 准备进入长模式 ---
; 1. 设置PAE和长模式使能位
mov eax, 10100000b ; PAE | PGE
mov cr4, eax
; 2. 加载页表(至少需要4级页表)
mov eax, pml4_table
mov cr3, eax
; 3. 设置EFER.LME(长模式使能)
mov ecx, 0xC0000080 ; EFER MSR
rdmsr
or eax, 0x00000100 ; 置LME位(第8位)
wrmsr
; 4. 开启分页,这也同时正式激活长模式
mov eax, cr0
or eax, 0x80000000 ; 置PG位
mov cr0, eax
; 5. 远跳转到64位代码段,刷新流水线
jmp 0x18:start_long
;------------------------ 简易4级页表 ------------------------
; 恒等映射最低2MB(虚拟地址=物理地址)
align 4096
pml4_table: dq pdp_table + 0x3 ; Present + ReadWrite
times 511 dq 0
pdp_table: dq pd_table + 0x3
times 511 dq 0
pd_table: dq 0x83 | (0x200000 * 0) ; 2MB大页,第0个2MB
times 511 dq 0
;------------------------ 第三阶段:64位长模式 ------------------------
[BITS 64]
start_long:
; 更新段寄存器(64位下大部分段基址被忽略)
mov ax, 0x20 ; 64位数据段选择子
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov rsp, 0x100000 ; 设置64位栈顶
; ===== 长模式代码开始 =====
; 这里写你的64位逻辑
; 你可以验证:访问64位寄存器、使用R8-R15、处理64位地址
; 示例:在屏幕左上角打印一个绿色字母 'O' 确认成功
mov rax, 0x00000000000F4F00 ; 字符'O' + 绿色背景
mov rdi, 0xB8000 ; VGA文本模式显存基址
mov [rdi], rax
; 停机
hlt_loop:
hlt
jmp hlt_loop
;------------------------ 填充和魔数 ------------------------
times 510 - ($ - $$) db 0
dw 0xAA55 ; 启动扇区签名
三、参考资料
1、 baidu -- chat.baidu.com
.
本文由 lnlidawei 原创、整理、转载,本文来自于【博客园】; 整理和转载的文章版权归属【原创作者】; 转载或引用时【请保留文章的来源信息】:https://www.cnblogs.com/lnlidawei/p/20119600

浙公网安备 33010602011771号