参照《ORANGE’S:一个操作系统的实现》
以下是pm.inc
- 1 DA_32 EQU 4000h ; 32 位段
- 2
- 3 DA_DPL0 EQU 00h ; DPL = 0
- 4 DA_DPL1 EQU 20h ; DPL = 1
- 5 DA_DPL2 EQU 40h ; DPL = 2
- 6 DA_DPL3 EQU 60h ; DPL = 3
- 7
- 8 ; 存储段描述符类型
- 9 DA_DR EQU 90h ; 存在的只读数据段类型值
- 10 DA_DRW EQU 92h ; 存在的可读写数据段属性值
- 11 DA_DRWA EQU 93h ; 存在的已访问可读写数据段类型值
- 12 DA_C EQU 98h ; 存在的只执行代码段属性值
- 13 DA_CR EQU 9Ah ; 存在的可执行可读代码段属性值
- 14 DA_CCO EQU 9Ch ; 存在的只执行一致代码段属性值
- 15 DA_CCOR EQU 9Eh ; 存在的可执行可读一致代码段属性值
- 16
- 17 ; 系统段描述符类型
- 18 DA_LDT EQU 82h ; 局部描述符表段类型值
- 19 DA_TaskGate EQU 85h ; 任务门类型值
- 20 DA_386TSS EQU 89h ; 可用 386 任务状态段类型值
- 21 DA_386CGate EQU 8Ch ; 386 调用门类型值
- 22 DA_386IGate EQU 8Eh ; 386 中断门类型值
- 23 DA_386TGate EQU 8Fh ; 386 陷阱门类型值
- 24
- 25 SA_RPL0 EQU 0 ; ┓
- 26 SA_RPL1 EQU 1 ; ┣ RPL
- 27 SA_RPL2 EQU 2 ; ┃
- 28 SA_RPL3 EQU 3 ; ┛
- 29
- 30 SA_TIG EQU 0 ; ┓TI
- 31 SA_TIL EQU 4 ; ┛
- 32
- 33 %macro Descriptor 3
- 34 dw %2 & 0FFFFh ; 段界限1
- 35 dw %1 & 0FFFFh ; 段基址1
- 36 db (%1 >> 16) & 0FFh ; 段基址2
- 37 dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性1 + 段界限2 + 属性2
- 38 db (%1 >> 24) & 0FFh ; 段基址3
- 39 %endmacro ; 共 8 字节
- 40 ;
- 41 ; 门
- 42 ; usage: Gate Selector, Offset, DCount, Attr
- 43 ; Selector: dw
- 44 ; Offset: dd
- 45 ; DCount: db
- 46 ; Attr: db
- 47 %macro Gate 4
- 48 dw (%2 & 0FFFFh) ; 偏移1
- 49 dw %1 ; 选择子
- 50 dw (%3 & 1Fh) | ((%4 << 8) & 0FF00h) ; 属性
- 51 dw ((%2 >> 16) & 0FFFFh) ; 偏移2
- 52 %endmacro ; 共 8 字节
以下是pmtest1.S
- 1 ; ==========================================
- 2 ; pmtest1.asm
- 3 ; 编译方法:nasm pmtest1.asm -o pmtest1.bin
- 4 ; ==========================================
- 5
- 6 %include "pm.inc" ; 常量, 宏, 以及一些说明
- 7
- 8 org 07c00h
- 9 jmp LABEL_BEGIN
- 10
- 11 [SECTION .gdt]
- 12 ; GDT
- 13 ; 段基址, 段界限 , 属性
- 14 LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
- 15 LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段
- 16 LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址
- 17 ; GDT 结束
- 18
- 19 GdtLen equ $ - LABEL_GDT ; GDT长度
- 20 GdtPtr dw GdtLen - 1 ; GDT界限
- 21 dd 0 ; GDT基地址
- 22
- 23 ; GDT 选择子
- 24 SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
- 25 SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
- 26 ; END of [SECTION .gdt]
- 27
- 28 [SECTION .s16]
- 29 [BITS 16]
- 30 LABEL_BEGIN:
- 31 mov ax, cs
- 32 mov ds, ax
- 33 mov es, ax
- 34 mov ss, ax
- 35 mov sp, 0100h
- 36
- 37 ; 初始化 32 位代码段描述符
- 38 xor eax, eax
- 39 mov ax, cs
- 40 shl eax, 4
- 41 add eax, LABEL_SEG_CODE32
- 42 mov word [LABEL_DESC_CODE32 + 2], ax
- 43 shr eax, 16
- 44 mov byte [LABEL_DESC_CODE32 + 4], al
- 45 mov byte [LABEL_DESC_CODE32 + 7], ah
- 46
- 47 ; 为加载 GDTR 作准备
- 48 xor eax, eax
- 49 mov ax, ds
- 50 shl eax, 4
- 51 add eax, LABEL_GDT ; eax <- gdt 基地址
- 52 mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址
- 53
- 54 ; 加载 GDTR
- 55 lgdt [GdtPtr]
- 56
- 57 ; 关中断
- 58 cli
- 59
- 60 ; 打开地址线A20
- 61 in al, 92h
- 62 or al, 00000010b
- 63 out 92h, al
- 64
- 65 ; 准备切换到保护模式
- 66 mov eax, cr0
- 67 or eax, 1
- 68 mov cr0, eax
- 69
- 70 ; 真正进入保护模式
- 71 jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs,
- 72 ; 并跳转到 Code32Selector:0 处
- 73 ; END of [SECTION .s16]
- 74
- 75
- 76 [SECTION .s32]; 32 位代码段. 由实模式跳入.
- 77 [BITS 32]
- 78
- 79 LABEL_SEG_CODE32:
- 80 mov ax, SelectorVideo
- 81 mov gs, ax ; 视频段选择子(目的)
- 82
- 83 mov edi, (80 * 11 + 79) * 2 ; 屏幕第 11 行, 第 79 列。
- 84 mov ah, 0Ch ; 0000: 黑底 1100: 红字
- 85 mov al, 'P'
- 86 mov [gs:edi], ax
- 87
- 88 ; 到此停止
- 89 jmp $
- 90
- 91 SegCode32Len equ $ - LABEL_SEG_CODE32
- 92 ; END of [SECTION .s32]
- 93
执行之后效果如下No bootable device.

初步分析认为是初次接触此类代码,没有理解关键变量的含义,囫囵运行之后无法达到预期目的。
第一感觉是以下的标签代码怎么不见了?
dw 0xaa55 ; boot record signature
查看对应的os.raw二进制文件,也不足512字节

正常的第一扇区应该是标准的512字节,如第一章的最简单OS的二进制内容如下

经过对第一扇区的补齐之后,奇迹般地,显示出了书中预想的红色的P
补齐后的pmtest2.S文件如下
- ; ==========================================
- ; pmtest1.asm
- ; 编译方法:nasm pmtest1.asm -o pmtest1.bin
- ; ==========================================
-
- %include "pm.inc" ; 常量, 宏, 以及一些说明
-
- org 07c00h
- jmp LABEL_BEGIN
-
- [SECTION .gdt]
- ; 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 ; 显存首地址
- ; GDT 结束
-
- GdtLen equ $ - LABEL_GDT ; GDT长度
- GdtPtr dw GdtLen - 1 ; GDT界限
- dd 0 ; GDT基地址
-
- ; GDT 选择子
- SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
- SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
- ; END of [SECTION .gdt]
-
- [SECTION .s16]
- [BITS 16]
- LABEL_BEGIN:
- mov ax, cs
- mov ds, ax
- mov es, ax
- mov ss, ax
- mov sp, 0100h
-
- ; 初始化 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
-
- ; 为加载 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 处
- ; END of [SECTION .s16]
-
-
- [SECTION .s32]; 32 位代码段. 由实模式跳入.
- [BITS 32]
-
- LABEL_SEG_CODE32:
- mov ax, SelectorVideo
- mov gs, ax ; 视频段选择子(目的)
-
- mov edi, (80 * 11 + 79) * 2 ; 屏幕第 11 行, 第 79 列。
- mov ah, 0Ch ; 0000: 黑底 1100: 红字
- mov al, 'P'
- mov [gs:edi], ax
-
- ; 到此停止
- jmp $
- times 22 db 0
- times 64 db 0
- times 64 db 0
- times 64 db 0
- times 64 db 0
- times 83 db 0
- dw 0xaa55
-
- SegCode32Len equ $ - LABEL_SEG_CODE32
- ; END of [SECTION .s32]
补齐后的二进制文件如下
