30天自制操作系统-第03天-进入32位模式并导入C语言

1.制作真正的IPL

    IPL(Initial Program Loader):启动程序装载器,软盘的第一个扇区,前面制作的IPL就显示一个Hello,world外什么事都没做,因此本节要在IPL里加入新的功能,读取下一个扇区(第二扇区)的内容

2.软盘的结构(1.44M)

    80个柱面(磁道),每个柱面毎·刺头18个扇区,2个磁头,总容量=80 X 18 X 2 X 512 = 1440 X 1024 =1.44M

    IPL位于C0-H0-S1(柱面-磁头-扇区), 因此下一个扇区就是C0-H0-S2

    本节前半部分讲述如何读取软盘,熟悉了解读取磁盘的调用即可

;读取软盘代码片段
MOV AX,0x0820  ;从第二个扇区开始读取,第一个扇区(IPL)已经被系统读取到7c00,这里从8200开始,原因之一在于便于计算hanbote.sys文件内容 (8000+4200=C200)的偏移量

MOV ES,AX

MOV BX,0 ;数据返回地址 ES:BX,此时0820:0000
MOV CH,0 ; 柱面0,也即磁道 ,CX中的6~15位代表柱面号,(其中,CL的6~7为柱面数的高两位,CH存低8位)

MOV CL,2 ; 2号扇区,CX中的0~5位代表扇区号,扇区编号从1开始
MOV DH,0 ; 磁头0

MOV DL,0x00 ; A驱动器,如果是硬盘,则7为置1,假如有A、B两软驱,C为硬盘,则C盘为0x80

MOV AH,0x02 ; AH=0x02 : 表示读取磁盘操作
MOV AL,1 ; 读取的扇区数量,此时1个 扇区
INT 0x13 ;调用磁盘BIOS
JC error ;jump if carry 如果进位值为1,说明出现了错误跳转到error输出错误信息

 

磁盘读取完成后:

      MOV [0x0ff0],CH ; IPL将读取到哪个柱面记录下来

      JMP 0xc200

  

3.初版操作系统文件:\03_day\harib00e\hanbote.nas:

     org 0xc200

     fin:

         HLT

         JMP fin

      编译成hanbote.sys

      执行Make install 发现 imgtol.com在win64下不能运行,因此进入Win2000虚拟机执行,执行成功

4.初版实验总结:

   一般向一个空软盘中保存文件时,文件名会写在0x002600以后的地方,文件的内容会写在0x004200以后的地方。当我们将操作系统本身的内容写到名为haribote.sys的文件中,再将其保存到磁盘映像里,确定好磁盘映像0x4200在内存中对应的地址位置,即可从启动区执行操作系统

    代码读取了10个柱面,即10 X 2 X 18 X 512=184320=180K = 0x2d000,已经远远超过0x4200 

    原以为会采用读取FAT表的读取文件,看来作者采用了取巧的方式。

5.第二版\03_day\harib00g:

   ipl10.nas

   haribote.nas:

         org 0xc200

         mov al,0x13                       ;VGA模式,320x200x8位彩色,对应的VRAM地址0xa0000

         mov ah,00                            

         int 0x10

     fin:

          hlt

          jmp fin

     注意知识点,int 10h功能调用

 6.第二版实验:

     执行 make run ,进入图形模式,屏幕全黑,没有光标闪烁,成功

 7.进入32位前的准备

   32位寄存器,EAX ,EBX等

   32位模式和16位模式:32位模式,指的是CPU的模式。CPU有32位和16两种模式。如果通过16位模式启动,使用AX和CX等寄存器会十分方便,而EAX和ECX等32位的寄存器就会比较     麻烦。在不同的模式下机器语言的命令代码不一样,解释方法不一样,故16位模式的机器语言在32位模式下不能运行。另外,32位下可以使用CPU的自我保护功能(识别出可疑的机器       语言并进行屏蔽,以避免破坏系统)。

8.第三版\03_day\harib00h:

 1)ipl10.nas

 2)haribote.nas代码
; BOOT_INFO
CYLS EQU 0x0ff0 ;设定启动区
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ; 关于颜色数目的信息。颜色的位数
SCRNX EQU 0x0ff4 ; 分辨率X
SCRNY EQU 0x0ff6 ; 分辨率Y
VRAM EQU 0x0ff8 ; 图形缓冲区的开始地址

ORG 0xc200 ; 程序被装载到内容中的地址
MOV AL,0x13 ; VGA显卡,320*200*8位彩色
MOV AH,0x00
INT 0x10
MOV BYTE [VMODE],8 ; 记录画面模式
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000
; 用BIOS取得键盘上各种LED指示灯的状态
MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL
fin:
HLT
JMP fin

3)bootpack.c:

void Harimain(void)

{

    fin:

       goto fin;/*这里不支持嵌入式汇编,此处就是死循环,耗电*/

}

4)将bootpack.c转换为sys文件的步骤:

a.使用ccl.exe将bootpack.c生成bootpack.gas
b.使用gas2nask.exe将bootpack.gas生成bootpack.nas
c.使用nask.exe将bootpack.nas生成bootpack.obj
d.使用obi2bim.exe将bootpack.obj生成bootpack.bim
e.使用bim2hrb.exe将bootpack.bim生成bootpack.hrb
f.使用copy指令将asmhead.bin与bootpack.hrb结合生成baribote.sys文件

5)执行make run 

9.最终版 第三版\03_day\harib00j:

   1)ipl10.nas

   2)asmhead.nas:

;此程序复杂,后面讲解

; haribote-os boot asm
; TAB=4

BOTPAK EQU 0x00280000 ; 加载bootpack
DSKCAC EQU 0x00100000 ; 磁盘缓存的位置
DSKCAC0 EQU 0x00008000 ; 磁盘缓存的位置(实模式)

; BOOT_INFO相关
CYLS EQU 0x0ff0 ; 引导扇区设置
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ; 关于颜色的信息
SCRNX EQU 0x0ff4 ; 分辨率X
SCRNY EQU 0x0ff6 ; 分辨率Y
VRAM EQU 0x0ff8 ; 图像缓冲区的起始地址

ORG 0xc200 ; 这个的程序要被装载的内存地址

; 画面モードを設定

MOV AL,0x13 ; VGA显卡,320x200x8bit
MOV AH,0x00
INT 0x10
MOV BYTE [VMODE],8 ; 屏幕的模式(参考C语言的引用)
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000

; 通过BIOS获取指示灯状态

MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL

; 防止PIC接受所有中断
; AT兼容机的规范、PIC初始化
; 然后之前在CLI不做任何事就挂起
; PIC在同意后初始化

MOV AL,0xff
OUT 0x21,AL
NOP ; 不断执行OUT指令
OUT 0xa1,AL

CLI ; 进一步中断CPU

; 让CPU支持1M以上内存、设置A20GATE

CALL waitkbdout
MOV AL,0xd1
OUT 0x64,AL
CALL waitkbdout
MOV AL,0xdf ; enable A20
OUT 0x60,AL
CALL waitkbdout

; 保护模式转换

[INSTRSET "i486p"] ; 说明使用486指令

LGDT [GDTR0] ; 设置临时GDT
MOV EAX,CR0
AND EAX,0x7fffffff ; 使用bit31(禁用分页)
OR EAX,0x00000001 ; bit0到1转换(保护模式过渡)
MOV CR0,EAX
JMP pipelineflush
pipelineflush:
MOV AX,1*8 ; 写32bit的段
MOV DS,AX
MOV ES,AX
MOV FS,AX
MOV GS,AX
MOV SS,AX

; bootpack传递

MOV ESI,bootpack ; 源
MOV EDI,BOTPAK ; 目标
MOV ECX,512*1024/4
CALL memcpy

; 传输磁盘数据

; 从引导区开始

MOV ESI,0x7c00 ; 源
MOV EDI,DSKCAC ; 目标
MOV ECX,512/4
CALL memcpy

; 剩余的全部

MOV ESI,DSKCAC0+512 ; 源
MOV EDI,DSKCAC+512 ; 目标
MOV ECX,0
MOV CL,BYTE [CYLS]
IMUL ECX,512*18*2/4 ; 除以4得到字节数
SUB ECX,512/4 ; IPL偏移量
CALL memcpy

; 由于还需要asmhead才能完成
; 完成其余的bootpack任务

; bootpack启动

MOV EBX,BOTPAK
MOV ECX,[EBX+16]
ADD ECX,3 ; ECX += 3;
SHR ECX,2 ; ECX /= 4;
JZ skip ; 传输完成
MOV ESI,[EBX+20] ; 源
ADD ESI,EBX
MOV EDI,[EBX+12] ; 目标
CALL memcpy
skip:
MOV ESP,[EBX+12] ; 堆栈的初始化
JMP DWORD 2*8:0x0000001b

waitkbdout:
IN AL,0x64
AND AL,0x02
JNZ waitkbdout ; AND结果不为0跳转到waitkbdout
RET

memcpy:
MOV EAX,[ESI]
ADD ESI,4
MOV [EDI],EAX
ADD EDI,4
SUB ECX,1
JNZ memcpy ; 运算结果不为0跳转到memcpy
RET
; memcpy地址前缀大小

ALIGNB 16
GDT0:
RESB 8 ; 初始值
DW 0xffff,0x0000,0x9200,0x00cf ; 写32bit位段寄存器
DW 0xffff,0x0000,0x9a28,0x0047 ; 可执行的文件的32bit寄存器(bootpack用)

DW 0
GDTR0:
DW 8*3-1
DD GDT0

ALIGNB 16
bootpack:

3)naskfunc.nas:

[FORMAT "WCOFF"] ; 制作目标文件的模式
[BITS 32] ; 制作32位模式用的机器语言
; 制作目标文件的信息
[FILE "naskfunc.nas"] ; 程序中包含的函数名
GLOBAL _io_hlt ;
; 以下是实际的函数
[SECTION .text]
_io_hlt: ; void io_hlt(void);C语言从外部调用
HLT
RET

4) bootpack.c:

/* 告诉C编译器,有一个函数在别的文件里 */

void io_hlt(void); /* 实现在naskfunc.nas*/

/* 是函数声明却不用{},而用;,这表示的意思是:
函数在别的文件中,你自己找一下 */

void HariMain(void)
{

fin:
io_hlt(); /* 执行naskfunc.nas中的_io_hlt函数 */
goto fin;

}

5) 执行make run,成功

10.知识点总结

     1) bios int 10h 中断

      2)C调用汇编

      3)进入保护模式(asmhead.nas中,但本节未讲解,留到后面章节)

      4)编译链接,最终形成sys文件,每个过程涉及的文件格式需要了解或者深入研究

 

 

 

          

 

posted @ 2022-04-21 18:10  煮酒熬码  阅读(449)  评论(0)    收藏  举报