haribote asmhead.nas 从实模式进入保护模式程序阅读注释

[ 1] haribote ipl09.nas 引导程序阅读注释

[2] haribote asmhead.nas  从实模式进入保护模式程序阅读注释
; haribote os(haribote.sys)
; |---------------------------------------------------|
; |             | header added |     bootpack.hrb     |
; | asmhead.nas |              |                      |
; |             |  by linker   | haribote os (C part) |
; |---------------------------------------------------|

; asmhead.nas经过多次拷贝后,内存空间分布大体如下。
; 0x00000|-------------------------|
;        |      infos by BIOS      |
; 100000h|=========================|
;        |  memory floppy storage  |
; 267fffh|=========================|
;        |          ...            |
; 280000h|=========================|
;        |   haribote os(C part)   |
; 2fffffh|=========================|
;        |  stack and global-data  |
;        |  haribote os (C part)   |
; 3fffffh|=========================|
;        |          ....           |
asmhead.nas
; haribote-os boot asm
; TAB=4

; asmhead.nas, 从x86实模式进入保护模式

; 告知编译器,本程序文件中包含i486指令
[INSTRSET "i486p"]


; 通过VBE指定 1024 x 768 x 8bit 彩色模式
VBEMODE EQU 0x105
;
; 列举下VBE所支持的显示模式
; 0x100 :  640 x  400 x 8bit 彩色
; 0x101 :  640 x  480 x 8bit 彩色
; 0x103 :  800 x  600 x 8bit 彩色
; 0x105 : 1024 x  768 x 8bit 彩色
; 0x107 : 1280 x 1024 x 8bit 彩色

; 进入保护模式后,
; asmhead.nas会将haribote除asmhead.nas的剩余程序
; 拷贝到扩展内存段中,并用一段内存用作软盘缓冲区。
;
; 以下地址分别代表
; haribote剩余程序将被拷贝到扩展内存中的起始地址,
; 存储整个软盘内容的起始地址,
; ipl09.nas所读取的软盘内容在内存中的起始地址(实际上是8200h)。
BOTPAK  EQU 0x00280000
DSKCAC  EQU 0x00100000
DSKCAC0 EQU 0x00008000

; 以下值皆为段地址0的段内偏移地址,
; 这些内存用于存储通过VBE或BIOS获取到的启动信息。
;
; 这些值分别用作
; 存储haribote OS所占总柱面数(由ipl09.nas设置),
; 保存键盘状态标志,
; 存储色素位数信息,
; 存储x分辨率,
; 存储y分辨率,
; 存储显存起始地址。
CYLS  EQU 0x0ff0
LEDS  EQU 0x0ff1
VMODE EQU 0x0ff2
SCRNX EQU 0x0ff4
SCRNY EQU 0x0ff6
VRAM  EQU 0x0ff8

; 告知编译器后续程序的段内偏移基址为0xc200
    ORG 0xc200

; 检查显卡是否支持VBE,
; 若不支持则跳转scrn320处使用BIOS设置画面显示。
;
; 若支持VBE,显卡能力集信息将输出到ES:DI指向的内存段。
    MOV AX,0x9000
    MOV ES,AX
    MOV DI,0
    MOV AX,0x4f00
    INT 0x10
    CMP AX,0x004f
    JNE scrn320   ; if (AX != 0x004f) goto scrn320;

; 检查VBE版本,小于v2.0的VBE不支持高分辨率显示,
; 则跳转scrn320处使用BIOS设置画面显示。
;
; 通过int 10h 4f00h 获取到的显卡能力集中,
; ES:DI+4处存储了版本信息。
    MOV AX,[ES:DI+4]
    CMP AX,0x0200
    JB  scrn320 ; if (AX < 0x0200) goto scrn320

; 通过VBE获取105h代表的显示模式
; 即1024 x 768 x 8bit 彩色显示模式相关信息。
;
; 若AX=0X004F则表示获取成功,
; 显示模式信息存于ES:DI指向内存,共256字节。
    MOV CX,VBEMODE
    MOV AX,0x4f01
    INT 0x10
    CMP AX,0x004f
    JNE scrn320

; 检查显示模式是否符合要求
    CMP BYTE [ES:DI+0x19],8
    JNE scrn320 ; 1个像素点所占位数需为8
    CMP BYTE [ES:DI+0x1b],4
    JNE scrn320 ; 显存显示图像的方式需为压缩像素方式
    MOV AX,[ES:DI+0x00]
    AND AX,0x0080
    JZ  scrn320 ; 需支持线性帧缓冲模式

; 通过VBE设置1024 x 768 x 8彩色显示模式。
;
; 作者实践需加上4000h才能设置成功,VBE手册中未提及。
    MOV BX,VBEMODE+0x4000
    MOV AX,0x4f02
    INT 0x10
; 设置成功后,
; 将105h显示模式相关参数存储到指定内存中供后续程序使用。
    MOV BYTE [VMODE],8 ; 8bits 1像素
    MOV AX,[ES:DI+0x12]
    MOV [SCRNX],AX  ; x分辨率
    MOV AX,[ES:DI+0x14]
    MOV [SCRNY],AX  ; y分辨率
    MOV EAX,[ES:DI+0x28]
    MOV [VRAM],EAX  ; 显存起始地址
    JMP keystatus

; 若显卡不支持VBE或高分辨率,
; 则通过BIOS设置320 x 200的显示画面。
scrn320:
    MOV AL,0x13
    MOV AH,0x00
    INT 0x10
; 将VGA显示模式属性保存到指定内存
; VGA显存地址空间为[0xa0000, 0xc0000)
    MOV BYTE [VMODE],8
    MOV WORD [SCRNX],320
    MOV WORD [SCRNY],200
    MOV DWORD [VRAM],0x000a0000


; 通过BIOS int 16 2 获取键盘状态存于0:LEDS内存中。
keystatus:
    MOV AH,0x02
    INT 0x16 ; keyboard BIOS
    MOV [LEDS],AL

; 若在此处不进行中断初始化可编程中断控制器,
; 则设置可编程中断控制器屏蔽所有中断。
; 禁止8259A-1和8258A-2所有中断。
    MOV AL,0xff
    OUT 0x21,AL
    NOP
    OUT 0xa1,AL
; 没有先经过ICW名字组初始化8259A,
; 怀疑此处下发OCW命令字的语句没有起作用,
; 起不处理中断的语句其实是下一个指令CLI。

    CLI ; 禁止CPU处理中断

; 开启第21根地址线,以访问1Mb之外的内存。
; 键盘控制器8042 P21引脚输出高电平将选通地址线A20。
    CALL waitkbdout ; 等待键盘输入寄存器空闲
; 往键盘控制器8042 64h端口写d1h命令,
; 表示即将往60h端口写数据输出到P2引脚。
    MOV  AL,0xd1
    OUT  0x64,AL
    
    CALL waitkbdout
; 往8042 60h端口写数据0xdf输出到P2引脚以使能A20。
    MOV  AL,0xdf
    OUT  0x60,AL
    CALL waitkbdout

; 设置GDT,
; 将设置在0:GDTR0处的GDT信息加载到GDTR寄存器中。
    LGDT [GDTR0]

; 禁止内存分页机制(CR0.bit[31]=0),
; 开启保护模式(CR0.bit[0]=1)。
    MOV EAX,CR0
    AND EAX,0x7fffffff
    OR  EAX,0x00000001
    MOV CR0,EAX
    JMP pipelineflush ; 刷新CPU预取指队列
;开启保护模式后,CPU以保护模式机制运行,在开启保护模式指令之后的指令
;都会以保护模式运行方式运行。x86CPU手册中提示,需在开启保护模式指令
;后立即跟一条跳转指令以刷新(丢掉)CPU预取指队列中的指令。
;
;刷新CPU预取指队列的跳转指令jmp pipelineflush 是在实模式下被读入到
;CPU中的,CPU在实模式下执行跳转指令时会刷新CPU预取指队列,
;同时jmp pipelineflush 这条跳转指令也能在保护模式下正确运行,因为它
;是一条段内跳转指令,其跳转与段地址无关,
;jmp pipelineflush功能相当于EIP += pipelineflush - EIP。

pipelineflush:
; 进入保护模式用跳转指令刷新CPU预取指队列后,
; 需设置各数据段寄存器指向数据内存段。
    MOV AX,1*8
    MOV DS,AX
    MOV ES,AX
    MOV FS,AX
    MOV GS,AX
    MOV SS,AX

; 将haribote OS 剩余程序即本程序标号bootpack
; 之后的512Kb内容拷贝到[0x280000,0x300000)内存段中。
;
; haribote OS 剩余程序没有512字节那么大,
; 拷贝512字节算是给haribote OS预留一些空间吧。
    MOV  ESI,bootpack
    MOV  EDI,BOTPAK
    MOV  ECX,512*1024/4
    CALL memcpy

; 接下来的两段子程序把软盘内容拷贝到[100000h, 128800h),
; 即作者把起始于100000h处的1.44Mb内存段用作存储软盘内容。
;
; 将引导区程序拷贝到[100000h, 100200h)内存段中。
    MOV  ESI,0x7c00
    MOV  EDI,DSKCAC
    MOV  ECX,512/4
    CALL memcpy

; 将[8200, 30800h)内存段内容拷贝到[100200h,128a00h)
    MOV  ESI,DSKCAC0+512
    MOV  EDI,DSKCAC+512
    MOV  ECX,0
    MOV  CL,BYTE [CYLS]
    IMUL ECX,512*18*2/4
    SUB  ECX,512/4 ; 减去引导程序大小
    CALL memcpy

; asmhead.nas与后续主要用C语言编写的程序
; 即被称作bootpack.hrb的部分共同构成haribote OS(haribote.sys)。
; C程序部分由作者改写的gcc编译器编译链接而成,
; 所得到的C语言可执行文件的头部包含了数据相关信息,
; 此处根据此头部作相关拷贝。
;
; haribote os(haribote.sys)
; |---------------------------------------------------|
; |             | header added |     bootpack.hrb     |
; | asmhead.nas |              |                      |
; |             |  by linker   | haribote os (C part) |
; |---------------------------------------------------|
    ; 从头部获取haribote C程序中的全局数据大小,
    ; 若haribote C程序中无全局数据则跳转执行skip处指令。
    MOV  EBX,BOTPAK
    MOV  ECX,[EBX+16]
    ADD  ECX,3  ; ECX += 3;
    SHR  ECX,2  ; ECX /= 4;
    JZ   skip
    ; 全局数据在haribote C程序中的偏移地址,
    ; 求得haribote C程序中全局数据的起始地址。
    MOV  ESI,[EBX+20]
    ADD  ESI,EBX
    ; haribote C程序栈顶初始位置
    MOV  EDI,[EBX+12]
    CALL memcpy ; 将haribote C程序数据部分拷贝到栈顶之后
skip:
    MOV ESP,[EBX+12] ; 初始化haribote os栈顶
    JMP DWORD 2*8:0x0000001b ; cs:eip=2*8:0x1b
;cs=2*8时是GDT[2]的选择符,
;这会使得CPU跳转执行GDT[2].base_addr+eip即0x280000+0x1b处指令,
;即跳转执行0x28001b处指令。
;
;haribote C程序偏移0x1b处为跳转指令的机器码,
;偏移0x1c处存了该跳转指令的目标地址 —— haribote C程序的入口地址。
;
;也就是说, 在执行经作者改编的gcc编译器和链接器所得到的C可执行程序时,
;需跳转到可执行程序0x1b偏移处才能执行到C程序的入口。
; 
;再者,作者在改编gcc编译器和链接器时,把C程序的入口函数也改为HariMain了。
;
;粗略理解这些内容是为了说明,
;jmp dword 2*8:0x1b最终会跳转到haribote os中的HariMain函数处,
;其被定义在bootpack.c中。


; asmhead.nas经过多次拷贝后,内存空间分布大体如下。
; 0x00000|-------------------------|
;        |      infos by BIOS      |
; 100000h|=========================|
;        |  memory floppy storage  |
; 267fffh|=========================|
;        |          ...            |
; 280000h|=========================|
;        |   haribote os(C part)   |
; 2fffffh|=========================|
;        |  stack and global-data  |
;        |  haribote os (C part)   |
; 3fffffh|=========================|
;        |          ....           |
; 作者没有公布所改编的编译器源码,头部信息如何组织具体不详。
; haribote os栈和数据区的内存地址空间是从作者的书中看过来的。
; 此文只粗略理解这个过程,能从这个过程梳理通相关原理就可以了,
; 具体细节也不去纠结了,以把更多精力放在将要盛装出场的HariMain吧。


; 等待键盘控制器8042输入寄存器空闲
waitkbdout:
    ; 读8042状态寄存器,
    ; 8042状态寄存器bit[1]=1表输入寄存器满,
    ; 尝试读一下输出寄存器以清空下输入寄存器内容,
    ; 若状态寄存器bit[1]=1则继续等待。
    IN  AL,0x64
    AND AL,0x02
    IN  AL,0x60
    JNZ waitkbdout
    RET

; while (ecx--)
;    mov es:edi, ds:esi
;    edi += 4
;    esi += 4
; 以4字节为单位, 拷贝ecx次,
; 拷贝ds:esi 指向内存段中数据到es:edi内存段。
memcpy:
    MOV EAX,[ESI]
    ADD ESI,4
    MOV [EDI],EAX
    ADD EDI,4
    SUB ECX,1
    JNZ memcpy  ; if (ecx != 0) goto memcpy;
    RET

; 告知编译器后续内容起始地址以16字节对齐
    ALIGNB  16
GDT0:
    RESB 8  ; GDT[0]保留为0

    ; GDT[1], 描述内存段为有效的数据内存段,
    ; 段内存基址=0, 段长=0xffffffff,dpl=0
    DW   0xffff,0x0000,0x9200,0x00cf

    ; GDT[2], 描述内存段为有效的可读可执行内存段,
    ; 段内存基址=0x00280000, 段长=0x7ffff字节;
    ; GDT[2]是专程为haribote 用C主写部分程序设计的。
    DW   0xffff,0x0000,0x9a28,0x0047

    DW  0

; GDT的长度和基址信息
GDTR0:
    ; GDT目前只有3个GDT描述符, 所以GDT限长3*8-1
    ; GDT内存地址。
    DW  8*3-1
    DD  GDT0

    ALIGNB  16
bootpack:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值