[手写OS]动手实现一个OS 之X86实模式下的汇编开发
x86实模式下 汇编开发是一个 intel x86实模式中的汇编程序开发类型。它涉及操纵几个16位处理器寄存器,并仅处理内存中的物理地址(与受保护模式相对)。
这种类型的编程中最广为人知的应用就是1980年代编写的DOS磁盘操作程序。
而所有现代的X86操作系统都是使用的保护模式,但是嘞,当计算机启动的时候,肯定不能是以保护模式启动的,通过实模式启动,并负责切换到保护模式,而这个从实模式切换到保护模式的这个过程,那就必须在实模式下运行。
寄存器
每个寄存器都专门用于某个任务,并且如果使用正确的寄存器,则处理该任务的操作通常会更有效地运行。
在实模式下的寄存器包括:
- 数据寄存器:
- AX,累加寄存器
- BX,基址寄存器
- CX,程序计数寄存器
- DX,数据寄存器
- 地址寄存器:
- SI,源地址寄存器
- DI,目的地址寄存器
- SP,栈顶指针寄存器
- BP,栈底指针寄存器
每一个数据寄存器都可以分为高八位寄存器和低八位寄存器,在一个16位寄存器可以一次解决8位数据,并且可以把这8位视为一个寄存器:例如,在AX寄存器中可分为高八位AH寄存器和低八位AL寄存器
每个都可以通过修改后缀的方式:将 X 扩展为 H 和 L,用来拆分出两个寄存器
总的来说,数据寄存器和地址寄存器统称为 通用寄存器
除此之外,通用寄存器还有如下:
- 段寄存器
- CS,代码段寄存器
- DS,数据段寄存器
- ES,特殊段寄存器
- FS,其他特殊段寄存器 (在Intel8086之前没有)
- GS,另一其他特殊段寄存器 (在Intel8086之前没有)
- SS,堆栈段寄存器
- 其他寄存器
- IP,指令指针寄存器
- FLAGS,标志寄存器
IP 寄存器指向程序中 处理器当前正在执行的代码位置,程序员不可以直接访问这个寄存器
FLAGS寄存器储存当前处理器执行的状态,这个寄存器中的每一位都是一个标志,每个标志都可以是1或0,设置或未设置,主要的标志含义为:携带Carry,溢出Overflow,零值Zero,单步执行Single Step
X86的体系结构中经常使用标志进行比较。比如两个寄存器 进行比较后如果有差别会拉高某个标志位,然后指令检查对应的标志位,如果发现这个标志位被拉高了就跳转
cmp ax,bx
jne 欲跳转的某个Label;
操作码助记符
在实模式中,允许的操作码如下:
aaa, aad, aam, aas, adc, add, and, call, cbw, clc, cld, cli, cmc, cmp, cmpsb, cmpsw, cwd, daa, das, dec, div, esc, hlt, idiv, imul, in, inc, int, into, iret, ja, jae, jb, jbe, jc, jcxz, je, jg, jge, jl, jle, jmp, jna, jnae, jnb, jnbe, jnc, jne, jng, jnge, jnl, jnle, jno, jnp, jns, jnz, jo, jp, jpe, jpo, js, jz, lahf, lds, lea, les, lock, lodsb, lodsw, loop, loope, loopne, loopnz, loopz, mov, movsb, movsw, mul, neg, nop, not, or, out, pop, popf, push, push, puchf, rcl, rcr, rep, repe, repne, repnz, repz, ret, rol, ror, sahf, sal, sar, sbb, scasb, scasw, shl, shr, stc, std, sti, stosb, stosw, sub, test, wait, xchg, xlat, xor
还有一些其他的操作码没有具体的操作码助记符,没有被记录进来。比如“0x0F”在8086处理器中被解释为POP CS
,x86家族其他处理器相比于早期处理器可能不会解释没有被记录的操作码。因此在使用没有文档解释的操作码时,你写的程序可能在之后的处理器上用不了
实模式的寻址
由于IA32和Intel64处理器的体系架构和其他处理器的架构不太一样,其他架构的可能线性地址空间就实模式地址空间,而IA32和Intel64处理器架构的还得分段。
分段模式主要是将线性地址分为两个部分: 段 + 偏移量
段地址指向一个64k的地址区间,从起始位置到指定位置的偏移量。要转换回线性地址,段地址向左移位4位,然后加上偏移量。
段寄存器:偏移寄存器 例如 DS:DX
段寄存器和通用寄存器的一些特殊组合指向重要地址
CS:IP 用来指向处理器获取下一字节代码的地址
SS:SP 指向的是入栈的最后一项的位置(一般表示栈顶)
DS:SI 通常用于指向即将被复制到 ES:DI 的数据