#include "asm.h"
#include "memlayout.h"
#include "mmu.h"
.code16
.globl start
start:
cli # 关中断, 防止干扰开启A20, 和保证设置GDT的完整性
# 初始化寄存器
xorw %ax,%ax # 将ax清零, 相似的方法如: movw $0, %ax , subw %ax, %ax
movw %ax,%ds # -> Data Segment
movw %ax,%es # -> Extra Segment
movw %ax,%ss # -> Stack Segment
# 开启A20
# A20地址线并不是打开保护模式的关键, 只是不开A20, 无法使用1MB以上的内存
# 因为历史原因, 在计算机启动时, 是处于实模式下, A20是关闭状态, 超出1MB内存会回卷
# 开启A20, 有三种方法,这只是其中一种
# 理论上讲, 打开A20的方法就是设置8042芯片输出端口(64h), 当该输出端口位1为1时就开启了A20信号线.
#但实际上, 当你向8042芯片输出端口进行写操作时, 在键盘缓冲区中, 或许还有别的数据尚未处理, 因此必须先处理这些数据, 这也是关中断的原因之一.
seta20.1:
#通过0x64端口, 读取8042芯片的8位状态寄存器(Status Register).
inb $0x64,%al
# 当输入缓存区为空时, 状态寄存器的值就会是0x2.
testb $0x2,%al
# 当状态寄存器的值不是0x2, 继续执行seta20.1,直到输入缓存器为空.
jnz seta20.1
# 将D1h写入端口0x64,表示准备写输出端口 , 随后通过60h端口写入的字节,会被放置在输出端口(64h)中。
movb $0xd1,%al
outb %al,$0x64
seta20.2:
inb $0x64,%al
testb $0x2,%al
jnz seta20.2
# 通过60端口,将0xdf写入输出端口(64h中)
movb $0xdf,%al # 0xdf -> port 0x60
outb %al,$0x60
# 加载GDT
# 后面有GDT的介绍, 共定义了三个段
# intel规定的全为零的一个, 索引号为0
# 基址为零, 长度为4G的代码段, 索引号为1
# 基址为零, 长度为4G的数据段, 索引号位2
lgdt gdtdesc
# 开启保护模式
# CR0中包含了6个预定义标志,0位是保护允许位PE(Protedted Enable),用于启动保护模式,如果PE位置1,则保护模式启动
movl %cr0, %eax
orl $CR0_PE, %eax # CR0_PE = 0x00000001
movl %eax, %cr0
# 进入保护模式, 跳转到start32
# SEG_KCODE=1, 左移三位是因为段索引的低3位, 有其他用处, 所以段索引号都乘以8, 即左移三位
# 使用ljmp是因为要将CS指向代码段, 启用保护模式下的段:偏移模式
ljmp $(SEG_KCODE<<3), $start32
.code32 # 32位代码段开始标志
start32:
# 初始化数据段寄存器
movw $(SEG_KDATA<<3), %ax # SEG_KDATA等于2
movw %ax, %ds # -> DS: Data Segment
movw %ax, %es # -> ES: Extra Segment
movw %ax, %ss # -> SS: Stack Segment
movw $0, %ax # 不使用FS和GS
movw %ax, %fs # -> FS
movw %ax, %gs # -> GS
# 设置栈顶, $start的地址是0x0000 7c00
movl $start, %esp
# 进入C语言编写的代码
call bootmain
# If bootmain returns (it shouldn't), trigger a Bochs
# breakpoint if running under Bochs, then loop.
# 在bochs中运行时用来调试用的代码
movw $0x8a00, %ax # 0x8a00 -> port 0x8a00
movw %ax, %dx
outw %ax, %dx
movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00
outw %ax, %dx
spin:
jmp spin
# Bootstrap GDT
.p2align 2 # force 4 byte alignment
gdt:
# 0x0000 0000 0000 0000
SEG_NULLASM # 全局描述符第0项, 8字节长的0, intel的规定
# 0x00cf 9a00 0000 ffff
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # 全局描述符第1项, 基址0, 长度4G, 代码段
# 0x00cf 9200 0000 ffff
SEG_ASM(STA_W, 0x0, 0xffffffff) # 全局描述符第2项, 基址0, 长度4G, 数据段
gdtdesc:
.word (gdtdesc - gdt - 1) # sizeof(gdt) - 1
.long gdt # address gdt