1.1.保护模式下屏幕显示
保护模式,32位下,屏幕显示
; 刚刚进入主引导程序时候,
; 主引导程序被加载到物理位置0x0000:0x7c00开始的地方
; cx此时的内容是0x0000
mov ax,cs
; 设置栈段
mov ss,ax
; 设置栈指针,初始时候指向高地址
mov sp,0x7c00
; 到指定物理内存位置取出2字节
mov ax,[cs:gdt_base+0x7c00]
; 到指定物理内存位置取出2字节
mov dx,[cs:gdt_base+0x7c00+0x02]
mov bx,16
div bx ; dx:ax / 16,商ax,余数dx
; 这是指定物理内存位置4字节构成的32位地址的段地址
mov ds,ax
; dx作为余数,其实是4字节对应32位地址的段内偏移
mov bx,dx
; 将指定物理内存位置4字节设置为0x00
mov dword [bx+0x00],0x00
; 将指定物理内存位置4字节设置为0x00
mov dword [bx+0x04],0x00
; 8字节数据展开
; 0x00 0x40 0x98 0x00 0x7c 0x00 0x01 0xff
; 高位<-----低位
; 段基地址31~24 G D/B L AVL 段界限19~16 P DPL S TYPE 段基地址23~16 段基地址15~0 段界限15~0
; 段基地址:0x00 0x00 0x7c 0x00,意思是段基地址为0x0000 7c00
; G为0,意思是段界限是字节为单位
; D/B为1,意思是段内的数据是32位的
; L为0,保留
; AVL为0,保留给软件
; 段界限0x0 0x01 0xff,0x001ff
; P为1,表示段的内容已经在物理内存了
; DPL为0,段的特权级
; S为1,非系统段
; TYPE为0x8,表示段可执行的代码段
; 将指定物理内存位置4字节设置为指定内容
mov dword [bx+0x08],0x7c0001ff
; 将指定物理内存位置4字节设置为指定内容
mov dword [bx+0x0c],0x00409800
; 8字节数据展开
; 0x00 0x40 0x92 0x0b 0x80 0x00 0xff 0xff
; 高位<-----低位
; 段基地址31~24 G D/B L AVL 段界限19~16 P DPL S TYPE 段基地址23~16 段基地址15~0 段界限15~0
; 段基地址:0x00 0x0b 0x80 0x00,意思是段基地址为0x000b 8000
; G为0,意思是段界限是字节为单位
; D/B为1,意思是段内的数据是32位的
; L为0,保留
; AVL为0,保留给软件
; 段界限0x0 0xff 0xff,0x0ffff,对于向上扩展的段,这个代表了段内尺寸-1
; P为1,表示段的内容已经在物理内存了
; DPL为0,段的特权级
; S为1,非系统段
; TYPE为0x2,表示段可读,可写数据段
mov dword [bx+0x10],0x8000ffff
mov dword [bx+0x14],0x0040920b
; 8字节数据展开
; 0x00 0x40 0x96 0x00 0x00 0x00 0x7a 0x00
; 高位<-----低位
; 段基地址31~24 G D/B L AVL 段界限19~16 P DPL S TYPE 段基地址23~16 段基地址15~0 段界限15~0
; 段基地址:0x00 0x00 0x00 0x00,意思是段基地址为0x0000 0000
; G为0,意思是段界限是字节为单位
; D/B为1,意思是段内的数据是32位的
; L为0,保留
; AVL为0,保留给软件
; 段界限0x0 0x7a 0x00,0x07a00,对向下扩展的段,这个代表了段的物理位置的下限
; 只要栈顶指针大于等于这个值,栈顶指针都是有效了。否则,就是无效的。
; P为1,表示段的内容已经在物理内存了
; DPL为0,段的特权级
; S为1,非系统段
; TYPE为0x6,表示段可读,可写向下扩展的栈段
mov dword [bx+0x18],0x00007a00
mov dword [bx+0x1c],0x00409600
; 将指定物理内存位置2字节设置为指定内容
mov word [cs: gdt_size+0x7c00],31
; 将指定物理内容位置处6字节传递给处理器
; 这样处理器就知道了程序的gdt的起始物理位置,gdt区域的尺寸
lgdt [cs: gdt_size+0x7c00]
; 从0x92读入1字节到al
in al,0x92
; 将读出字节第1位【从0计算】设置为1
or al,0000_0010B
; 将修改后内容写入0x92
; 这样做,可以让进程可以使用32位地址线来表示32位地址空间
; 未这样做的时候,32位地址线只有低20位存储有效地址
out 0x92,al
; 禁止中断
cli
; 读取cr0寄存器内容
mov eax,cr0
; 设置最低位为1
or eax,1
; 这样将使得进程进入保护模式运行
mov cr0,eax
; 需要用一个跳转进行实模式和保护模式的过渡
; 两个模式主要区别是:
; 实模式访问数据,访问代码采用段地址*0x10+段内偏移来实现。段寄存器存储的就是段地址。段缺乏保护。
; 保护模式,每个段首先通过一个64位描述符描述符其起始位置,尺寸,其他属性。段寄存器存储的是段选择子。
; 通过选择子找到段的64位描述符。【前提是已经使用lgdt告知处理器段描述符的信息】
; 从64位描述符中提取32位起始地址+偏移地址来访问数据,访问代码。
; 且对段的访问存在保护。
; 0x0008,对应的段选择子内索引是1【从0开始】
jmp dword 0x0008:flush
[bits 32]
flush:
mov cx,00000000000_10_000B
; 对应的段选择子内索引是2
mov ds,cx
; 设置物理内存位置值
mov byte [0x00],'P'
mov byte [0x02],'r'
mov byte [0x04],'o'
mov byte [0x06],'t'
mov byte [0x08],'e'
mov byte [0x0a],'c'
mov byte [0x0c],'t'
mov byte [0x0e],' '
mov byte [0x10],'m'
mov byte [0x12],'o'
mov byte [0x14],'d'
mov byte [0x16],'e'
mov byte [0x18],' '
mov byte [0x1a],'O'
mov byte [0x1c],'K'
; 上述设置了显存,达到在屏幕显示字符效果
mov cx,00000000000_11_000B
; 对应的段选择子内索引是3
mov ss,cx
; 栈段一开始偏移位于高地址处
; esp合法的变动范围是[0x7a00, 0x7c00]
mov esp,0x7c00
mov ebp,esp
; 入栈存放1字节
push byte '?'
; 将ebp减去4
sub ebp,4
; 比较栈顶与ebp是否相等
cmp ebp,esp
; 不相等则跳转
jnz ghalt
; 相等则出栈,得到4字节数据
pop eax
; 取其中1字节设置到指定物理内存位置
mov [0x1e],al
ghalt:
; 停机,等待被中断唤醒。
hlt
; gdt尺寸
gdt_size dw 0
; gdt起始物理内存位置
gdt_base dd 0x00007e00
; 填充
times 510-($-$$) db 0
; 引导块完整性要求
db 0x55,0xaa
1.2.保护模式下冒泡排序
保护模式,32位下冒泡排序
; 进入主引导程序
; 此时程序在内存0x0000:0x7c00开始的一块位置
; cs此时是0x0000
mov eax,cs
; 设置栈段
mov ss,eax
; 设置栈段指针,初始位于最大地址处
mov sp,0x7c00
;用4字节物理内存数据设置eax
mov eax,[cs:pgdt+0x7c00+0x02]
; edx设置为0
xor edx,edx
mov ebx,16
; edx:eax / 16
; 商在eax,余数在edx
div ebx
; 用商设置dx,这是4字节物理地址的段地址
mov ds,eax
; 余数设置ebx,这是4字节物理地址的段内偏移
mov ebx,edx
mov dword [ebx+0x00],0x00000000
; 将指定物理内存位置4字节设置为指定值
mov dword [ebx+0x04],0x00000000
; 8字节数据展开
; 0x00 0xcf 0x92 0x00 0x00 0x00 0xff 0xff
; 高位<-----低位
; 段基地址31~24 G D/B L AVL 段界限19~16 P DPL S TYPE 段基地址23~16 段基地址15~0 段界限15~0
; 段基地址:0x00 0x00 0x00 0x00,意思是段基地址为0x0000 0000
; G为1,意思是段界限是页为单位
; D/B为1,意思是段内的数据是32位的
; L为0,保留
; AVL为0,保留给软件
; 段界限0xf 0xff 0xff,0xfffff,
; P为1,表示段的内容已经在物理内存了
; DPL为0,段的特权级
; S为1,非系统段
; TYPE为0x2,表示段可读,可写的数据段
mov dword [ebx+0x08],0x0000ffff
mov dword [ebx+0x0c],0x00cf9200
; 8字节数据展开
; 0x00 0x40 0x98 0x00 0x7c 0x00 0x01 0xff
; 高位<-----低位
; 段基地址31~24 G D/B L AVL 段界限19~16 P DPL S TYPE 段基地址23~16 段基地址15~0 段界限15~0
; 段基地址:0x00 0x00 0x7c 0x00,意思是段基地址为0x0000 7c00
; G为0,意思是段界限是字节为单位
; D/B为1,意思是段内的数据是32位的
; L为0,保留
; AVL为0,保留给软件
; 段界限0x0 0x01 0xff,0x001ff,
; P为1,表示段的内容已经在物理内存了
; DPL为0,段的特权级
; S为1,非系统段
; TYPE为0x8,表示段是只执行的代码段
mov dword [ebx+0x10],0x7c0001ff
mov dword [ebx+0x14],0x00409800
; 8字节数据展开
; 0x00 0x40 0x92 0x00 0x7c 0x00 0x01 0xff
; 高位<-----低位
; 段基地址31~24 G D/B L AVL 段界限19~16 P DPL S TYPE 段基地址23~16 段基地址15~0 段界限15~0
; 段基地址:0x00 0x00 0x7c 0x00,意思是段基地址为0x0000 7c00
; G为0,意思是段界限是字节为单位
; D/B为1,意思是段内的数据是32位的
; L为0,保留
; AVL为0,保留给软件
; 段界限0x0 0x01 0xff,0x001ff,
; P为1,表示段的内容已经在物理内存了
; DPL为0,段的特权级
; S为1,非系统段
; TYPE为0x2,表示段是可读,可写的数据段
mov dword [ebx+0x18],0x7c0001ff
mov dword [ebx+0x1c],0x00409200
; 8字节数据展开
; 0x00 0xcf 0x96 0x00 0x7c 0x00 0xff 0xfe
; 高位<-----低位
; 段基地址31~24 G D/B L AVL 段界限19~16 P DPL S TYPE 段基地址23~16 段基地址15~0 段界限15~0
; 段基地址:0x00 0x00 0x7c 0x00,意思是段基地址为0x0000 7c00
; G为1,意思是段界限是页为单位
; D/B为1,意思是段内的数据是32位的
; L为0,保留
; AVL为0,保留给软件
; 段界限0xf 0xff 0xfe,0xffffe,
; P为1,表示段的内容已经在物理内存了
; DPL为0,段的特权级
; S为1,非系统段
; TYPE为0x6,表示段是可读,可写的向下扩展的栈段
; 栈段的段界限以页为单位下
; 如何确定栈地址和合法范围
; 对于ESP有以下限制
; 描述符中的段界限值*0x1000 + 0xFFF + 1 <= [ESP-操作数长度] <= 0xFFFFFFFF
; 实际的物理地址 = 栈基地址+ESP
mov dword [ebx+0x20],0x7c00fffe
mov dword [ebx+0x24],0x00cf9600
; 将指定物理内存位置2字节设置为39
mov word [cs: pgdt+0x7c00],39
; 将gdt起始物理内存位置和尺寸告知处理器
lgdt [cs: pgdt+0x7c00]
in al,0x92
or al,0000_0010B
; 打开a20地址线,
; 此后,进程可使用32为地址空间
out 0x92,al
; 禁止中断
cli
mov eax,cr0
or eax,1
; 开启保护模式
mov cr0,eax
; 需要用一个跳转指定完成实模式到保护模式的切换
jmp dword 0x0010:flush
[bits 32]
flush:
; 0x00 0b0001 1000
; 段选择子是3
mov eax,0x0018
mov ds,eax
mov eax,0x0008
; 段选择子是1
mov es,eax
; fs新的段寄存器
mov fs,eax
; gs新的段寄存器
mov gs,eax
; 段选择子是4
mov eax,0x0020
; 设置栈段
mov ss,eax
; 设置栈顶指向
;xor esp,esp
mov esp, 0xFFFFFFFF
; 设置显存
mov dword [es:0x0b8000],0x072e0750
; 设置显存
mov dword [es:0x0b8004],0x072e074d
; 设置显存
mov dword [es:0x0b8008],0x07200720
; 设置显存
mov dword [es:0x0b800c],0x076b076f
;
; 字符串尺寸-1
mov ecx,pgdt-string-1
@@1:
; 双重循环,内部循环执行当前位置到后续位置冒泡
; 对字符串执行冒泡排序
; 入栈
push ecx
; 设置bx为0
xor bx,bx
@@2:
; 取得2字节
mov ax,[string+bx]
; 低字节,高字节比较
cmp ah,al
; 高字节>=低字节
jge @@3
; 本来高字节<低字节,
; 交换后,高字节>低字节
xchg al,ah
; 将2字节从寄存器拷贝到物理内存
mov [string+bx],ax
@@3:
; 增加bx,使得其指向下一个字符位置
inc bx
; 循环指定次数
loop @@2
; 恢复ecx
pop ecx
loop @@1
; 恢复ecx
mov ecx,pgdt-string
; 清空ebx
xor ebx,ebx
@@4:
mov ah,0x07
mov al,[string+ebx]
; 从第二行显示排序后字符串
mov [es:0xb80a0+ebx*2],ax
inc ebx
loop @@4
hlt
string db 's0ke4or92xap3fv8giuzjcy5l1m7hd6bnqtw.'
pgdt dw 0
dd 0x00007e00
times 510-($-$$) db 0
db 0x55,0xaa