[自制简单操作系统] 1、从0-1到汇编再到c语言的奥秘

 


目录:

1、用0-1编写最简单的操作系统

2、用汇编改写上面0-1程序

  2.1 只用DB的汇编改写版  2.2 加入RESB汇编的改写版  2.3 进一步使用汇编替换0-1文件  2.4 核心程序也用汇编改写  2.5 向汇编程序中加入IPL(启动程序装载器)  2.6 从启动区执行操作系统(读盘的应用)

3、汇编和C语言混合开发

  3.1 32位开发及C语言混合开发引入 3.2 汇编引入C语言(用汇编写C语言函数)  3.3 C语言实现内存写入  3.4 C语言指针的强大  3.5 色号设定与调色板 3.6 简单界面实现


 

1、用0-1编写最简单的操作系统

>_<" 用二进制编辑器(Binary Editor),输入下面的内容(这里是16进制的)

PS: 如上图,蓝色部分要仔细,下面一直到168000这个地址都是00,在0001F0和001400附近还有些地方不是0,要把他们改过来,检查一遍,保存为helloos.img

>_<" 将这个文件写入软盘用来启动电脑就能显示“hello,world”这个字符串。

PS: 上面界面是用了一个PC模拟器


 

2、用汇编改写上面0-1程序

>_<" 这里就把上述超长的0-1文件改成汇编语言文件helloos.nas,然后调用nask.exe编译器将.nas文件编译为.img~剩下的就不用说了~

2.1 只用DB的汇编改写版

1 DB    0xeb, 0x4e, 0x90, 0x48, 0x45, 0x4c, 0x4c, 0x4f
2 DB    0x49, 0x50, 0x4c, 0x00, 0x02, 0x01, 0x01, 0x00
3 DB    0x02, 0xe0, 0x00, 0x40, 0x0b, 0xf0, 0x09, 0x00
4 DB    0x12, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00
5 DB    0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x29, 0xff
6 (为节省纸张,这里省略18万4314行)
7 DB    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

PS:DB是data byte的缩写,就是往文件里直接写入1字节的指令~这样就实现了汇编实现最上面的0-1文件,但是貌似看起来更长了....(如果足够牛B的话,只用DB就能做出任何文件)

 

2.2 加入RESB汇编的改写版

 1 DB    0xeb, 0x4e, 0x90, 0x48, 0x45, 0x4c, 0x4c, 0x4f
 2 DB    0x49, 0x50, 0x4c, 0x00, 0x02, 0x01, 0x01, 0x00
 3 DB    0x02, 0xe0, 0x00, 0x40, 0x0b, 0xf0, 0x09, 0x00
 4 DB    0x12, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00
 5 DB    0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x29, 0xff
 6 DB    0xff, 0xff, 0xff, 0x48, 0x45, 0x4c, 0x4c, 0x4f
 7 DB    0x2d, 0x4f, 0x53, 0x20, 0x20, 0x20, 0x46, 0x41
 8 DB    0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00
 9 RESB    16
10 DB    0xb8, 0x00, 0x00, 0x8e, 0xd0, 0xbc, 0x00, 0x7c
11 DB    0x8e, 0xd8, 0x8e, 0xc0, 0xbe, 0x74, 0x7c, 0x8a
12 DB    0x04, 0x83, 0xc6, 0x01, 0x3c, 0x00, 0x74, 0x09
13 DB    0xb4, 0x0e, 0xbb, 0x0f, 0x00, 0xcd, 0x10, 0xeb
14 DB    0xee, 0xf4, 0xeb, 0xfd, 0x0a, 0x0a, 0x68, 0x65
15 DB    0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72
16 DB    0x6c, 0x64, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00  ;到000080前8个的内容,后8个和000090之后填0,部分做修改
17 RESB    368
18 DB    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa  ;这就是0001F0处非0的内容
19 DB    0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
20 RESB    4600
21 DB    0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00  ;伙计发现没,这就是001400地址处的内容
22 RESB    1469432

PS: 这里多加入了一个汇编指令RESB,就使文件大致清新多了~这里RESB指令是reserve byte的缩写,如果想从某个字节开始空出10个字节就能写成RESB 10,这样我们就节约了大约10万行代码~

 

2.3 进一步使用汇编替换0-1文件

>_<" 虽然上面把10几万行的0-1改写的只有20几行,但是还是无法理解其意思,下面进行优化,使我们能理解代码的意思~

 1 ; hello-os
 2 ; TAB=4
 3 
 4 ; 以下段落是标准FAT12格式软盘专用代码
 5     DB    0xeb, 0x4e, 0x90  ; 注意和我们写的0-1代码,以及上面几个版本的对比一下,你就会明白的
 6     DB    "HELLOIPL"        ; 启动区的名称可以是任意字符串(8字节)
 7     DW    512              ; 每个扇区(sector)的大小必须为512字节
 8     DB    1               ; 簇(cluster)的大小必须为1个扇区
 9     DW    1               ; FAT的起始位置(一般从第一个扇区开始)
10     DB    2               ; FAT的个数(必须为2)
11     DW    224              ; 根目录的大小(一般设为244项)
12     DW    2880             ; 该磁盘的的大小(必须为2880扇区)
13     DB    0xf0             ; 磁盘的种类(必须为0xfd)
14     DW    9                ; FAT的长度(必须为9扇区)
15     DW    18               ; 一个磁道(track)有几个扇区(必须为18)
16     DW    2                ; 磁头数(必须为2)
17     DD    0                ; 不使用分区(必须为0)
18     DD    2880             ; 重写一次磁盘大小
19     DB    0,0,0x29         ; 意义不明,固定
20     DD    0xffffffff       ; (可能是)卷标号码
21     DB    "HELLO-OS   "    ; 磁盘名称(11字节)
22     DB    "FAT12   "       ; 磁盘格式名称(8字节)
23     RESB    18             ; 先腾出18字节
24 
25 ; 程序主体
26     DB    0xb8, 0x00, 0x00, 0x8e, 0xd0, 0xbc, 0x00, 0x7c
27     DB    0x8e, 0xd8, 0x8e, 0xc0, 0xbe, 0x74, 0x7c, 0x8a
28     DB    0x04, 0x83, 0xc6, 0x01, 0x3c, 0x00, 0x74, 0x09
29     DB    0xb4, 0x0e, 0xbb, 0x0f, 0x00, 0xcd, 0x10, 0xeb
30     DB    0xee, 0xf4, 0xeb, 0xfd
31 
32 ; 信息显示部分
33     DB    0x0a, 0x0a        ; 2个换行
34     DB    "hello, world"
35     DB    0x0a              ; 换行
36     DB    0
37 
38     RESB    0x1fe-$            ; 填写0x00直到0x001fe
39     DB    0x55, 0xaa
40 
41 ; 以下是启动区以外部分的输出
42     DB    0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
43     RESB    4600
44     DB    0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
45     RESB    1469432

PS: 不用解释,程序主体放在下面讲,如何转换为容易理解的汇编~

 

2.4 核心程序也用汇编改写

>_<" 这里主要对上面改进版汇编的25~36行进行改写为更容易理解的汇编程序(最开头也改动一点点),其他一样:

 1 ; hello-os
 2 ; TAB=4
 3         ORG        0x7c00            ; 指明程序的装载地址
 4 ; 以下段落是标准FAT12格式软盘专用代码
 5         JMP        entry
 6         DB        0x90
 7         DB        "HELLOIPL"        ; 启动区的名称可以是任意字符串(8字节)
 8         DW        512                ; 每个扇区(sector)的大小必须为512字节
 9         DB        1                ; 簇(cluster)的大小必须为1个扇区
10         DW        1                ; FAT的起始位置(一般从第一个扇区开始)
11         DB        2                ; FAT的个数(必须为2)
12         DW        224                ; 根目录的大小(一般设为244项)
13         DW        2880            ; 该磁盘的的大小(必须为2880扇区)
14         DB        0xf0            ; 磁盘的种类(必须为0xfd)
15         DW        9                ; FAT的长度(必须为9扇区)
16         DW        18                ; 一个磁道(track)有几个扇区(必须为18)
17         DW        2                ; 磁头数(必须为2)
18         DD        0                ; 不使用分区(必须为0)
19         DD        2880            ; 重写一次磁盘大小
20         DB        0,0,0x29        ; 意义不明,固定
21         DD        0xffffffff        ; (可能是)卷标号码
22         DB        "HELLO-OS   "    ; 磁盘名称(11字节)
23         DB        "FAT12   "        ; 磁盘格式名称(8字节)
24         RESB    18                ; 先腾出18字节
25 
26 ; 程序核心
27 entry:
28         MOV        AX,0            ; 初始化寄存器
29         MOV        SS,AX
30         MOV        SP,0x7c00
31         MOV        DS,AX
32         MOV        ES,AX
33 
34         MOV        SI,msg          ;主程序就是把msg的内容给SI然后循环逐个输出
35 putloop:
36         MOV        AL,[SI]
37         ADD        SI,1            ; SI++
38         CMP        AL,0
39         JE        fin
40         MOV        AH,0x0e            ; 显示一个文字
41         MOV        BX,15            ; 指定字符颜色
42         INT        0x10            ; 调用显卡BIOS(汇编中经常会用到的一种调用形式)
43         JMP        putloop
44 fin:
45         HLT                        ; 让CPU停止等待指令(和在main函数的结尾写一个getchar()类似)
46         JMP        fin                ; 无限循环
47 
48 msg:
49         DB        0x0a, 0x0a        ; 2个回车
50         DB        "hello, world"
51         DB        0x0a            ; 回车
52         DB        0
53 
54         RESB    0x7dfe-$        ; 填充0
55 
56         DB        0x55, 0xaa
57 
58 ; 以下是启动区以外部分的输出
59         DB        0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
60         RESB    4600
61         DB        0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
62         RESB    1469432

PS: 这里我们可以方便的看出来,原来主程序的功能就是把msg里面的字符先传给SI然后SI++遍历循环输出的意思,哈哈,终于能完整的从0-1转到汇编然后理解0-1的意思啦~

PS: 这里刚开始的125字节就是启动区125字节内容,其地址为0x00007c00-0x00007dff,所以第3行指明程序加载地址~

 

2.5 向汇编程序中加入IPL(启动程序装载器)

>_<" 上面的汇编实现了将msg的字符串显示到显示屏上,但是这里的msg是本来保存在内存中的,一般CPU的内存是很小的,如果把大量的信息(如整个操作系统)都保存在这里肯定是不合理的,因此就需要把这部分内容存储在其他上(如内存条,这里采用软盘)!因此,我们就要在核心程序初始化之后从软盘读取数据放到内存在执行~(下面是调用INT 0x13读取磁盘的相关代码)

 1 ; 读磁盘(从软盘中读数据装到内存中0x8200--0x83ff  125字节的地方)
 2 MOV        AX,0x0820        ;ES:BX=缓冲地址
 3 MOV        ES,AX
 4 MOV        CH,0            ; 柱面0
 5 MOV        DH,0            ; 磁头0
 6 MOV        CL,2            ; 扇区2
 7 
 8 MOV        AH,0x02            ; AH=0x02 : 读盘
 9 MOV        AL,1            ; 1个扇区
10 MOV        BX,0
11 MOV        DL,0x00            ; A驱动器
12 INT        0x13            ; 调用磁盘BIOS
13 JC        error

 >_<" 修改后的汇编代码如下:(这里从磁盘读取数据装到0x8200--0x83ff 125字节的地方,如果读取出错就跳转到error,显示读取出错这个内容~

 1 ; haribote-ipl
 2 ; TAB=4
 3         ORG        0x7c00            ; 指明程序装载地址
 4 
 5 ; 以下这段是FAT12格式软盘专用代码  0x7c00--0x7dff 125字节 用于启动区
 6         JMP        entry
 7         DB        0x90
 8         DB        "HARIBOTE"        ; 启动区的名字可以是任意的,但必须是8字节
 9         DW        512                ; 每个扇区(sector)的大小必须为512字节
10         DB        1                ; 簇(cluster)的大小必须为1个扇区
11         DW        1                ; FAT的起始位置(一般从第一个扇区开始)
12         DB        2                ; FAT的个数(必须为2)
13         DW        224                ; 根目录的大小(一般设为244项)
14         DW        2880            ; 该磁盘的的大小(必须为2880扇区)
15         DB        0xf0            ; 磁盘的种类(必须为0xfd)
16         DW        9                ; FAT的长度(必须为9扇区)
17         DW        18                ; 一个磁道(track)有几个扇区(必须为18)
18         DW        2                ; 磁头数(必须为2)
19         DD        0                ; 不使用分区(必须为0)
20         DD        2880            ; 重写一次磁盘大小
21         DB        0,0,0x29        ; 意义不明,固定
22         DD        0xffffffff        ; (可能是)卷标号码
23         DB        "HARIBOTEOS "    ; 磁盘名称(11字节)
24         DB        "FAT12   "        ; 磁盘格式名称(8字节)
25         RESB    18                ; 先腾出18字节
26 
27 ; 程序核心
28 entry:
29         MOV        AX,0            ; 初始化寄存器
30         MOV        SS,AX
31         MOV        SP,0x7c00
32         MOV        DS,AX
33 
34 ; 读磁盘(从软盘中读数据装到内存中0x8200--0x83ff  125字节的地方)
35         MOV        AX,0x0820        ;ES:BX=缓冲地址
36         MOV        ES,AX
37         MOV        CH,0            ; 柱面0
38         MOV        DH,0            ; 磁头0
39         MOV        CL,2            ; 扇区2
40 
41         MOV        AH,0x02            ; AH=0x02 : 读盘
42         MOV        AL,1            ; 1个扇区
43         MOV        BX,0
44         MOV        DL,0x00            ; A驱动器
45         INT        0x13            ; 调用磁盘BIOS
46         JC        error
47  
48 fin:
49         HLT                        ; 让CPU停止等待指令
50         JMP        fin                ; 无限循环
51 
52 error:
53         MOV        SI,msg            ; 循环输出msg里面的内容     
54         
55 putloop:
56         MOV        AL,[SI]
57         ADD        SI,1            ; 给SI加1
58         CMP        AL,0
59         JE        fin
60         MOV        AH,0x0e            ; 显示一个文字
61         MOV        BX,15            ; 指定字符颜色
62         INT        0x10            ; 调用显卡BIOS
63         JMP        putloop  
64         
65 msg:
66         DB        0x0a, 0x0a        ; 换行2次
67         DB        "load error"
68         DB        0x0a            ; 换行
69         DB        0
70 
71         RESB    0x7dfe-$        ; 0x7dfe傑偱傪0x00偱杽傔傞柦椷
72 
73         DB        0x55, 0xaa
从磁盘读取内容IPL

 

2.6 从启动区执行操作系统(读盘的应用)

>_<" 软盘这种东西不可靠,因此我们要多次读盘尝试,如果多次之后还不行再放弃读盘,读盘代码改动如下:

 1 ; 读磁盘(从软盘中读数据装到内存中0x8200--0x83ff  125字节的地方)
 2         MOV        AX,0x0820        ;ES:BX=缓冲地址
 3         MOV        ES,AX
 4         MOV        CH,0            ; 柱面0
 5         MOV        DH,0            ; 磁头0
 6         MOV        CL,2            ; 扇区2   
 7         
 8         MOv     SI,0            ; 记录失败次数的寄存器
 9 retry:
10         MOV        AH,0x02            ; AH=0x02 : 读盘
11         MOV        AL,1            ; 1个扇区
12         MOV        BX,0
13         MOV        DL,0x00            ; A驱动器
14         INT        0x13            ; 调用磁盘BIOS
15         JNC     fin             ; 没出错就跳转到fin
16         ADD     SI,1              ; SI++
17         CMP     SI,5            ; 比较SI和5
18         JAE     error           ; SI>=5时,跳转到error
19         MOV     AH,0x00
20         MOV     DL,0x00         ; A驱动器
21         INT     0x13            ; 重置驱动器
22         JMP     retry
5次读盘的核心代码
 1 ; haribote-ipl
 2 ; TAB=4
 3         ORG        0x7c00            ; 指明程序装载地址
 4 
 5 ; 以下这段是FAT12格式软盘专用代码  0x7c00--0x7dff 125字节 用于启动区
 6         JMP        entry
 7         DB        0x90
 8         DB        "HARIBOTE"        ; 启动区的名字可以是任意的,但必须是8字节
 9         DW        512                ; 每个扇区(sector)的大小必须为512字节
10         DB        1                ; 簇(cluster)的大小必须为1个扇区
11         DW        1                ; FAT的起始位置(一般从第一个扇区开始)
12         DB        2                ; FAT的个数(必须为2)
13         DW        224                ; 根目录的大小(一般设为244项)
14         DW        2880            ; 该磁盘的的大小(必须为2880扇区)
15         DB        0xf0            ; 磁盘的种类(必须为0xfd)
16         DW        9                ; FAT的长度(必须为9扇区)
17         DW        18                ; 一个磁道(track)有几个扇区(必须为18)
18         DW        2                ; 磁头数(必须为2)
19         DD        0                ; 不使用分区(必须为0)
20         DD        2880            ; 重写一次磁盘大小
21         DB        0,0,0x29        ; 意义不明,固定
22         DD        0xffffffff        ; (可能是)卷标号码
23         DB        "HARIBOTEOS "    ; 磁盘名称(11字节)
24         DB        "FAT12   "        ; 磁盘格式名称(8字节)
25         RESB    18                ; 先腾出18字节
26 
27 ; 程序核心
28 entry:
29         MOV        AX,0            ; 初始化寄存器
30         MOV        SS,AX
31         MOV        SP,0x7c00
32         MOV        DS,AX
33 
34 ; 读磁盘(从软盘中读数据装到内存中0x8200--0x83ff  125字节的地方)
35         MOV        AX,0x0820        ;ES:BX=缓冲地址
36         MOV        ES,AX
37         MOV        CH,0            ; 柱面0
38         MOV        DH,0            ; 磁头0
39         MOV        CL,2            ; 扇区2   
40         
41         MOv     SI,0            ; 记录失败次数的寄存器
42 retry:
43         MOV        AH,0x02            ; AH=0x02 : 读盘
44         MOV        AL,1            ; 1个扇区
45         MOV        BX,0
46         MOV        DL,0x00            ; A驱动器
47         INT        0x13            ; 调用磁盘BIOS
48         JNC     fin             ; 没出错就跳转到fin
49         ADD     SI,1              ; SI++
50         CMP     SI,5            ; 比较SI和5
51         JAE     error           ; SI>=5时,跳转到error
52         MOV     AH,0x00
53         MOV     DL,0x00         ; A驱动器
54         INT     0x13            ; 重置驱动器
55         JMP     retry
56  
57 fin:
58         HLT                        ; 让CPU停止等待指令
59         JMP        fin                ; 无限循环
60 
61 error:
62         MOV        SI,msg            ; 循环输出msg里面的内容     
63         
64 putloop:
65         MOV        AL,[SI]
66         ADD        SI,1            ; 给SI加1
67         CMP        AL,0
68         JE        fin
69         MOV        AH,0x0e            ; 显示一个文字
70         MOV        BX,15            ; 指定字符颜色
71         INT        0x10            ; 调用显卡BIOS
72         JMP        putloop  
73         
74 msg:
75         DB        0x0a, 0x0a        ; 换行2次
76         DB        "load error"
77         DB        0x0a            ; 换行
78         DB        0
79 
80         RESB    0x7dfe-$        ; 0x7dfe傑偱傪0x00偱杽傔傞柦椷
81 
82         DB        0x55, 0xaa
83 
84 完整的代码
5次读盘的全部代码

>_<" 趁着上面的尽头,咱们直接写个从扇区2读到扇区18的代码吧!

PS:这里采用的思路是:把扇区2->18的内容读到内存,内存刚开始的地址为0x0820,每次读一个扇区向后移动0x0200个地址(即:125字节)

 1 ; 读磁盘(从软盘中读数据装到内存中0x8200--0x83ff  125字节的地方)
 2         MOV        AX,0x0820        ; ES:BX=缓冲地址
 3         MOV        ES,AX
 4         MOV        CH,0            ; 柱面0
 5         MOV        DH,0            ; 磁头0
 6         MOV        CL,2            ; 扇区2   
 7 readloop:        
 8         MOv     SI,0            ; 记录失败次数的寄存器
 9 retry:
10         MOV        AH,0x02            ; AH=0x02 : 读盘
11         MOV        AL,1            ; 1个扇区
12         MOV        BX,0
13         MOV        DL,0x00            ; A驱动器
14         INT        0x13            ; 调用磁盘BIOS
15         JNC     next            ; 没出错就跳转到next继续读下一个做准备
16         ADD     SI,1            ; SI++
17         CMP     SI,5            ; 比较SI和5
18         JAE     error           ; SI>=5时,跳转到error
19         MOV     AH,0x00
20         MOV     DL,0x00         ; A驱动器
21         INT     0x13            ; 重置驱动器
22         JMP     retry           
23 next:   
24         MOV     AX,ES           ; 把内存地址后移0x200
25         ADD     AX,0x0020
26         MOV     ES,AX           ; 因为没有ADD ES,0x200指令,所以这里绕个弯
27         ADD     CL,1            ; 往CL里加1 (所读扇区标号,开始是2,见初始化部分)
28         CMP     CL,18           ; 和18比较 
29         JBE     readloop        ; 小于18就跳转到readloop继续读
读取到18扇区的核心代码
 1 ; haribote-ipl
 2 ; TAB=4
 3         ORG        0x7c00            ; 指明程序装载地址
 4 
 5 ; 以下这段是FAT12格式软盘专用代码  0x7c00--0x7dff 125字节 用于启动区
 6         JMP        entry
 7         DB        0x90
 8         DB        "HARIBOTE"        ; 启动区的名字可以是任意的,但必须是8字节
 9         DW        512                ; 每个扇区(sector)的大小必须为512字节
10         DB        1                ; 簇(cluster)的大小必须为1个扇区
11         DW        1                ; FAT的起始位置(一般从第一个扇区开始)
12         DB        2                ; FAT的个数(必须为2)
13         DW        224                ; 根目录的大小(一般设为244项)
14         DW        2880            ; 该磁盘的的大小(必须为2880扇区)
15         DB        0xf0            ; 磁盘的种类(必须为0xfd)
16         DW        9                ; FAT的长度(必须为9扇区)
17         DW        18                ; 一个磁道(track)有几个扇区(必须为18)
18         DW        2                ; 磁头数(必须为2)
19         DD        0                ; 不使用分区(必须为0)
20         DD        2880            ; 重写一次磁盘大小
21         DB        0,0,0x29        ; 意义不明,固定
22         DD        0xffffffff        ; (可能是)卷标号码
23         DB        "HARIBOTEOS "    ; 磁盘名称(11字节)
24         DB        "FAT12   "        ; 磁盘格式名称(8字节)
25         RESB    18                ; 先腾出18字节
26 
27 ; 程序核心
28 entry:
29         MOV        AX,0            ; 初始化寄存器
30         MOV        SS,AX
31         MOV        SP,0x7c00
32         MOV        DS,AX
33 
34 ; 读磁盘(从软盘中读数据装到内存中0x8200--0x83ff  125字节的地方)
35         MOV        AX,0x0820        ; ES:BX=缓冲地址
36         MOV        ES,AX
37         MOV        CH,0            ; 柱面0
38         MOV        DH,0            ; 磁头0
39         MOV        CL,2            ; 扇区2   
40 readloop:        
41         MOv     SI,0            ; 记录失败次数的寄存器
42 retry:
43         MOV        AH,0x02            ; AH=0x02 : 读盘
44         MOV        AL,1            ; 1个扇区
45         MOV        BX,0
46         MOV        DL,0x00            ; A驱动器
47         INT        0x13            ; 调用磁盘BIOS
48         JNC     next            ; 没出错就跳转到next继续读下一个做准备
49         ADD     SI,1            ; SI++
50         CMP     SI,5            ; 比较SI和5
51         JAE     error           ; SI>=5时,跳转到error
52         MOV     AH,0x00
53         MOV     DL,0x00         ; A驱动器
54         INT     0x13            ; 重置驱动器
55         JMP     retry           
56 next:   
57         MOV     AX,ES           ; 把内存地址后移0x200
58         ADD     AX,0x0020
59         MOV     ES,AX           ; 因为没有ADD ES,0x200指令,所以这里绕个弯
60         ADD     CL,1            ; 往CL里加1 (所读扇区标号,开始是2,见初始化部分)
61         CMP     CL,18           ; 和18比较 
62         JBE     readloop        ; 小于18就跳转到readloop继续读
63  
64 fin:
65         HLT                        ; 让CPU停止等待指令
66         JMP        fin                ; 无限循环
67 
68 error:
69         MOV        SI,msg            ; 循环输出msg里面的内容     
70         
71 putloop:
72         MOV        AL,[SI]
73         ADD        SI,1            ; 给SI加1
74         CMP        AL,0
75         JE        fin
76         MOV        AH,0x0e            ; 显示一个文字
77         MOV        BX,15            ; 指定字符颜色
78         INT        0x10            ; 调用显卡BIOS
79         JMP        putloop  
80         
81 msg:
82         DB        0x0a, 0x0a        ; 换行2次
83         DB        "load error"
84         DB        0x0a            ; 换行
85         DB        0
86 
87         RESB    0x7dfe-$        ; 0x7dfe傑偱傪0x00偱杽傔傞柦椷
88 
89         DB        0x55, 0xaa
读取到18扇区的完整代码

>_<" 趁热打铁,C0-H0-S18的下一扇区就是磁盘的反面C0-H1-S1,这次是要从C0-H0-S2---->C0-H0-S3----->C9-H1-S18

 1 ; 读磁盘(从软盘中读数据装到内存中0x8200--0x83ff  125字节的地方)
 2         MOV        AX,0x0820        ; ES:BX=缓冲地址
 3         MOV        ES,AX
 4         MOV        CH,0            ; 柱面0
 5         MOV        DH,0            ; 磁头0
 6         MOV        CL,2            ; 扇区2   
 7 readloop:        
 8         MOv     SI,0            ; 记录失败次数的寄存器
 9 retry:
10         MOV        AH,0x02            ; AH=0x02 : 读盘
11         MOV        AL,1            ; 1个扇区
12         MOV        BX,0
13         MOV        DL,0x00            ; A驱动器
14         INT        0x13            ; 调用磁盘BIOS
15         JNC     next            ; 没出错就跳转到next继续读下一个做准备
16         ADD     SI,1            ; SI++
17         CMP     SI,5            ; 比较SI和5
18         JAE     error           ; SI>=5时,跳转到error
19         MOV     AH,0x00
20         MOV     DL,0x00         ; A驱动器
21         INT     0x13            ; 重置驱动器
22         JMP     retry           
23 next:   
24         MOV     AX,ES           ; 把内存地址后移0x200
25         ADD     AX,0x0020
26         MOV     ES,AX           ; 因为没有ADD ES,0x200指令,所以这里绕个弯
27         ADD     CL,1            ; 往CL里加1 (所读扇区标号,开始是2,见初始化部分)
28         CMP     CL,18           ; 和18比较 
29         JBE     readloop        ; 小于18就跳转到readloop继续读 
30         MOV     CL,1            ; 扇区1
31         ADD     DH,1            ; 磁头+1
32         CMP     DH,2            ; 判断磁头是否超过2
33         JB      readloop        ; 没有超过就继续读
34         MOV     DH,0            ; 超过2就转为0
35         ADD     CH,1            ; CH记录读取的柱面数
36         CMP     CH,CYLS         ; CYLS在前面定义 CYLS EQU 10
37         JB      readloop
读取10个柱面的核心代码
 1 ; haribote-ipl
 2 ; TAB=4
 3 CYLS    EQU        10                ; 定义读的柱面数
 4         ORG        0x7c00            ; 指明程序装载地址
 5 
 6 ; 以下这段是FAT12格式软盘专用代码  0x7c00--0x7dff 125字节 用于启动区
 7         JMP        entry
 8         DB        0x90
 9         DB        "HARIBOTE"        ; 启动区的名字可以是任意的,但必须是8字节
10         DW        512                ; 每个扇区(sector)的大小必须为512字节
11         DB        1                ; 簇(cluster)的大小必须为1个扇区
12         DW        1                ; FAT的起始位置(一般从第一个扇区开始)
13         DB        2                ; FAT的个数(必须为2)
14         DW        224                ; 根目录的大小(一般设为244项)
15         DW        2880            ; 该磁盘的的大小(必须为2880扇区)
16         DB        0xf0            ; 磁盘的种类(必须为0xfd)
17         DW        9                ; FAT的长度(必须为9扇区)
18         DW        18                ; 一个磁道(track)有几个扇区(必须为18)
19         DW        2                ; 磁头数(必须为2)
20         DD        0                ; 不使用分区(必须为0)
21         DD        2880            ; 重写一次磁盘大小
22         DB        0,0,0x29        ; 意义不明,固定
23         DD        0xffffffff        ; (可能是)卷标号码
24         DB        "HARIBOTEOS "    ; 磁盘名称(11字节)
25         DB        "FAT12   "        ; 磁盘格式名称(8字节)
26         RESB    18                ; 先腾出18字节
27 
28 ; 程序核心
29 entry:
30         MOV        AX,0            ; 初始化寄存器
31         MOV        SS,AX
32         MOV        SP,0x7c00
33         MOV        DS,AX
34 
35 ; 读磁盘(从软盘中读数据装到内存中0x8200--0x83ff  125字节的地方)
36         MOV        AX,0x0820        ; ES:BX=缓冲地址
37         MOV        ES,AX
38         MOV        CH,0            ; 柱面0
39         MOV        DH,0            ; 磁头0
40         MOV        CL,2            ; 扇区2   
41 readloop:        
42         MOv     SI,0            ; 记录失败次数的寄存器
43 retry:
44         MOV        AH,0x02            ; AH=0x02 : 读盘
45         MOV        AL,1            ; 1个扇区
46         MOV        BX,0
47         MOV        DL,0x00            ; A驱动器
48         INT        0x13            ; 调用磁盘BIOS
49         JNC     next            ; 没出错就跳转到next继续读下一个做准备
50         ADD     SI,1            ; SI++
51         CMP     SI,5            ; 比较SI和5
52         JAE     error           ; SI>=5时,跳转到error
53         MOV     AH,0x00
54         MOV     DL,0x00         ; A驱动器
55         INT     0x13            ; 重置驱动器
56         JMP     retry           
57 next:   
58         MOV     AX,ES           ; 把内存地址后移0x200
59         ADD     AX,0x0020
60         MOV     ES,AX           ; 因为没有ADD ES,0x200指令,所以这里绕个弯
61         ADD     CL,1            ; 往CL里加1 (所读扇区标号,开始是2,见初始化部分)
62         CMP     CL,18           ; 和18比较 
63         JBE     readloop        ; 小于18就跳转到readloop继续读 
64         MOV     CL,1            ; 扇区1
65         ADD     DH,1            ; 磁头+1
66         CMP     DH,2            ; 判断磁头是否超过2
67         JB      readloop        ; 没有超过就继续读
68         MOV     DH,0            ; 超过2就转为0
69         ADD     CH,1            ; CH记录读取的柱面数
70         CMP     CH,CYLS         ; CYLS在前面定义 CYLS EQU 10
71         JB      readloop
72         
73 fin:
74         HLT                        ; 让CPU停止等待指令
75         JMP        fin                ; 无限循环
76 
77 error:
78         MOV        SI,msg            ; 循环输出msg里面的内容     
79         
80 putloop:
81         MOV        AL,[SI]
82         ADD        SI,1            ; 给SI加1
83         CMP        AL,0
84         JE        fin
85         MOV        AH,0x0e            ; 显示一个文字
86         MOV        BX,15            ; 指定字符颜色
87         INT        0x10            ; 调用显卡BIOS
88         JMP        putloop  
89         
90 msg:
91         DB        0x0a, 0x0a        ; 换行2次
92         DB        "load error"
93         DB        0x0a            ; 换行
94         DB        0
95 
96         RESB    0x7dfe-$        ; 0x7dfe傑偱傪0x00偱杽傔傞柦椷
97 
98         DB        0x55, 0xaa
读取10个柱面的全部代码

 

3、汇编和C语言混合开发

3.1 32位开发及C语言混合开发引入

>_<" 现在我们已经学会如何从磁盘读数据放到内存中了,那么下面就是如何将操作系统本身内容写进磁盘映像,然后我们从启动区执行这个内容就行了~

>_<" 前两部分我们已经把操作系统的引导程序写好了,即是最终的读取10个柱面的全部代码,我们通过编译就能把其编译为磁盘镜像文件(即最上面看的0-1文件);接下来新建一个haribote.nas汇编文件,在里面先写上如下代码,这就是一个简单的操作系统内容文件,用nask编译为.sys格式的文件,保存到磁盘映像.img里(这个过程比较复杂,其实有比较好的软件可以帮助我们直接解决上述保存到磁盘映像的问题:edimg.exe) 。这里文件保存到映像文件时有一个规律:

  • 文件名会在0x002600以后的地方出现
  • 文件的内容会写在0x004200后的地方

 根据上面的规律,我们就可以将操作系统本身的内容写到名为haribote.sys文件中,再把他们保存到磁盘映像里,然后我们从启动区执行这个haribotes.sys就行了。

1 fin:
2         HLT                        ; 让CPU停止等待指令
3         JMP        fin                ; 无限循环

>_<" 那么该怎样才能执行磁盘映像上位于0x004200号地址的程序呢?我们在第二节已经介绍,程序是从启动区开始执行,把磁盘上的内容装在到内存0x8000号地址,所以磁盘0x4200处的内容位于内存0x8000+0x4200=0xc200号地址处。 这样我们在haribote.nas里加上ORG 0xc200,然后在第二节最后读取10个柱面的全部代码(下面命名为ipl.nas)加上JMP 0xc200。

1 ; haribote-os
2 ; TAB=4
3 
4         ORG        0xc200            
5 fin:
6         HLT
7         JMP        fin

这样将上面两个文件编译并合并为映像文件并运行,什么都没有发生,那么我们想看看程序到底有没有运行,该则么办呢?其实很简单,就像我们刚学C++的时候让程序输出个hello world,但是这里,因为我们上两节已经实现了这个功能,再玩一遍没啥意思,于是,这里来个高级一点的:这次切换一下画面模式(因为我们最终要做成windows那样地画面),因此需要把在我们的haribote.nas内添加一些内容(主要是调用显卡BIOS的函数,具体搜索INT 0x10):

 1 ; haribote-os
 2 ; TAB=4
 3 
 4         ORG        0xc200            ; 这个程序要被装在到程序的什么位置
 5 
 6         MOV        AL,0x13            ; VGA显卡,320X200X8位彩色
 7         MOV        AH,0x00
 8         INT        0x10
 9 fin:
10         HLT
11         JMP        fin

这样如果程序正常运行,画面将是一片漆黑,光标会消失。另外,我们将ipl.nas的文件名改成ipl10.nas(提醒大家,这个程序只能读入10个柱面),另外,想要把磁盘装在内容的结束地址告诉haribote.sys,所以我们要在JMP 0xc200之前加上一行命令,将CYLS的值写到地址0x0ff0中,这样启动程序就修改好了~

1 ; 磁盘内容装载内容的结束地址告诉haribote.sys
2 
3         MOV        [0x0ff0],CH        ; 将CYLS的值写到内存地址0x0ff0中 
4         JMP        0xc200
  1 ; haribote-ipl
  2 ; TAB=4
  3 CYLS    EQU        10                ; 定义读的柱面数
  4         ORG        0x7c00            ; 指明程序装载地址
  5 
  6 ; 以下这段是FAT12格式软盘专用代码  0x7c00--0x7dff 125字节 用于启动区
  7         JMP        entry
  8         DB        0x90
  9         DB        "HARIBOTE"        ; 启动区的名字可以是任意的,但必须是8字节
 10         DW        512                ; 每个扇区(sector)的大小必须为512字节
 11         DB        1                ; 簇(cluster)的大小必须为1个扇区
 12         DW        1                ; FAT的起始位置(一般从第一个扇区开始)
 13         DB        2                ; FAT的个数(必须为2)
 14         DW        224                ; 根目录的大小(一般设为244项)
 15         DW        2880            ; 该磁盘的的大小(必须为2880扇区)
 16         DB        0xf0            ; 磁盘的种类(必须为0xfd)
 17         DW        9                ; FAT的长度(必须为9扇区)
 18         DW        18                ; 一个磁道(track)有几个扇区(必须为18)
 19         DW        2                ; 磁头数(必须为2)
 20         DD        0                ; 不使用分区(必须为0)
 21         DD        2880            ; 重写一次磁盘大小
 22         DB        0,0,0x29        ; 意义不明,固定
 23         DD        0xffffffff        ; (可能是)卷标号码
 24         DB        "HARIBOTEOS "    ; 磁盘名称(11字节)
 25         DB        "FAT12   "        ; 磁盘格式名称(8字节)
 26         RESB    18                ; 先腾出18字节
 27 
 28 ; 程序核心
 29 entry:
 30         MOV        AX,0            ; 初始化寄存器
 31         MOV        SS,AX
 32         MOV        SP,0x7c00
 33         MOV        DS,AX
 34 
 35 ; 读磁盘(从软盘中读数据装到内存中0x8200--0x83ff  125字节的地方)
 36         MOV        AX,0x0820        ; ES:BX=缓冲地址
 37         MOV        ES,AX
 38         MOV        CH,0            ; 柱面0
 39         MOV        DH,0            ; 磁头0
 40         MOV        CL,2            ; 扇区2   
 41 readloop:        
 42         MOv     SI,0            ; 记录失败次数的寄存器
 43 retry:
 44         MOV        AH,0x02            ; AH=0x02 : 读盘
 45         MOV        AL,1            ; 1个扇区
 46         MOV        BX,0
 47         MOV        DL,0x00            ; A驱动器
 48         INT        0x13            ; 调用磁盘BIOS
 49         JNC     next            ; 没出错就跳转到next继续读下一个做准备
 50         ADD     SI,1            ; SI++
 51         CMP     SI,5            ; 比较SI和5
 52         JAE     error           ; SI>=5时,跳转到error
 53         MOV     AH,0x00
 54         MOV     DL,0x00         ; A驱动器
 55         INT     0x13            ; 重置驱动器
 56         JMP     retry           
 57 next:   
 58         MOV     AX,ES           ; 把内存地址后移0x200
 59         ADD     AX,0x0020
 60         MOV     ES,AX           ; 因为没有ADD ES,0x200指令,所以这里绕个弯
 61         ADD     CL,1            ; 往CL里加1 (所读扇区标号,开始是2,见初始化部分)
 62         CMP     CL,18           ; 和18比较 
 63         JBE     readloop        ; 小于18就跳转到readloop继续读 
 64         MOV     CL,1            ; 扇区1
 65         ADD     DH,1            ; 磁头+1
 66         CMP     DH,2            ; 判断磁头是否超过2
 67         JB      readloop        ; 没有超过就继续读
 68         MOV     DH,0            ; 超过2就转为0
 69         ADD     CH,1            ; CH记录读取的柱面数
 70         CMP     CH,CYLS         ; CYLS在前面定义 CYLS EQU 10
 71         JB      readloop
 72 
 73 ; 磁盘内容装载内容的结束地址告诉haribote.sys
 74         MOV        [0x0ff0],CH        ; 将CYLS的值写到内存地址0x0ff0中 
 75         JMP        0xc200        
 76 
 77 error:
 78         MOV        SI,msg            ; 循环输出msg里面的内容     
 79         
 80 putloop:
 81         MOV        AL,[SI]
 82         ADD        SI,1            ; 给SI加1
 83         CMP        AL,0
 84         JE        fin
 85         MOV        AH,0x0e            ; 显示一个文字
 86         MOV        BX,15            ; 指定字符颜色
 87         INT        0x10            ; 调用显卡BIOS
 88         JMP        putloop                       
 89 fin:
 90         HLT                        ; 让CPU停止等待指令
 91         JMP        fin                ; 无限循环
 92         
 93 msg:
 94         DB        0x0a, 0x0a        ; 换行2次
 95         DB        "load error"
 96         DB        0x0a            ; 换行
 97         DB        0
 98 
 99         RESB    0x7dfe-$        ; 0x7dfe傑偱傪0x00偱杽傔傞柦椷
100 
101         DB        0x55, 0xaa
ipl10.nas(启动程序)

PS: 这里我们把启动区里与haribote.sys无关的前后部分也读了进来,所以启动很慢,但是以后会有用的~

>_<" 现在,汇编语言开发终于可以告一段落啦~,接下来我们主要以C语言开发为 主了~因为这里用的是32位C语言编译器(32位模式比16位的要好很多,这里不详细说明了),但是32位模式就不能调用BIOS功能了(这是因为BIOS是由16位机器语言写的,如果我们有什么事情想用BIOS来做,就要全部放在开头做,一旦进入32位模式就不能调用BIOS函数了(其实也有32位返回16位的方法)。再回头说说使用BIOS的事情,在上面我们已经把画面模式设定已经做完了,接下来还想从BIOS获取键盘状态(即:NumLock是ON还是OFF),这次只修改了haribote.nas:

 1 ; haribote-os
 2 ; TAB=4
 3 
 4 ; BOOT_INFO相关
 5 CYLS    EQU        0x0ff0            ; 设定启动区
 6 LEDS    EQU        0x0ff1
 7 VMODE    EQU        0x0ff2            ; 关于颜色的信息颜色的位数
 8 SCRNX    EQU        0x0ff4            ; 分辨率X
 9 SCRNY    EQU        0x0ff6            ; 分辨率Y
10 VRAM    EQU        0x0ff8            ; 图像缓冲区的开始地址
11 
12         ORG        0xc200            ; 这个程序要装载到什么地方
13 
14         MOV        AL,0x13            ; VGA显卡,320X200X8位色彩
15         MOV        AH,0x00
16         INT        0x10
17         MOV        BYTE [VMODE],8    ; 记录画面模式
18         MOV        WORD [SCRNX],320
19         MOV        WORD [SCRNY],200
20         MOV        DWORD [VRAM],0x000a0000
21 
22 ; 用BIOS获得键盘上各种LED指示灯的状态
23 
24         MOV        AH,0x02
25         INT        0x16             ; keyboard BIOS
26         MOV        [LEDS],AL
27 
28 fin:
29         HLT
30         JMP        fin

这个程序一看就明白,其实就是设置画面模式之后,将设置的信息保存到内存中(以后可能要设置不同的画面模式,就要把现在的设置信息保存起来以备后用);[VRAM]里保存的是0xa0000,在电脑的世界里,VRAM是指显卡内存(Video RAM)也就是显示画面的内存,这一块可以像一般的内存一样存储数据,但VRAM的功能不仅限于此,它的各个地址都对应着画面上的像素,可以利用这一机制在画面上绘制出五颜六色的图案~

 

3.2 汇编引入C语言(用汇编写C语言函数)

>_<" 终于准备就绪了,现在我们直接切换到32位模式,然后运行C语言程序,这次要修改很多:首先是haribote.sys,它的前半部分是用汇编写的,后半部分是用C语言写的,所以为了方便把文件名也从haribote.nas也随之改成了asmhead.nas,并且为了调用C语言在前面加了100行左右的汇编代码(今后再详细介绍这100行代码)下面先把到这里全部的代码贴出来(我猜很多人已经晕了,不知道到底是哪些文件了)

  1 ; haribote-ipl
  2 ; TAB=4
  3 CYLS    EQU        10                ; 定义读的柱面数
  4         ORG        0x7c00            ; 指明程序装载地址
  5 
  6 ; 以下这段是FAT12格式软盘专用代码  0x7c00--0x7dff 125字节 用于启动区
  7         JMP        entry
  8         DB        0x90
  9         DB        "HARIBOTE"         ; 启动区的名字可以是任意的,但必须是8字节
 10         DW        512                ; 每个扇区(sector)的大小必须为512字节
 11         DB        1                  ; 簇(cluster)的大小必须为1个扇区
 12         DW        1                  ; FAT的起始位置(一般从第一个扇区开始)
 13         DB        2                  ; FAT的个数(必须为2)
 14         DW        224                ; 根目录的大小(一般设为244项)
 15         DW        2880               ; 该磁盘的的大小(必须为2880扇区)
 16         DB        0xf0               ; 磁盘的种类(必须为0xfd)
 17         DW        9                  ; FAT的长度(必须为9扇区)
 18         DW        18                 ; 一个磁道(track)有几个扇区(必须为18)
 19         DW        2                  ; 磁头数(必须为2)
 20         DD        0                  ; 不使用分区(必须为0)
 21         DD        2880               ; 重写一次磁盘大小
 22         DB        0,0,0x29           ; 意义不明,固定
 23         DD        0xffffffff         ; (可能是)卷标号码
 24         DB        "HARIBOTEOS "      ; 磁盘名称(11字节)
 25         DB        "FAT12   "         ; 磁盘格式名称(8字节)
 26         RESB    18                   ; 先腾出18字节
 27 
 28 ; 程序核心
 29 entry:
 30         MOV        AX,0              ; 初始化寄存器
 31         MOV        SS,AX
 32         MOV        SP,0x7c00
 33         MOV        DS,AX
 34 
 35 ; 读磁盘(从软盘中读数据装到内存中0x8200--0x83ff  125字节的地方)
 36         MOV        AX,0x0820         ; ES:BX=缓冲地址
 37         MOV        ES,AX
 38         MOV        CH,0              ; 柱面0
 39         MOV        DH,0              ; 磁头0
 40         MOV        CL,2              ; 扇区2   
 41 readloop:        
 42         MOv     SI,0                 ; 记录失败次数的寄存器
 43 retry:
 44         MOV        AH,0x02           ; AH=0x02 : 读盘
 45         MOV        AL,1              ; 1个扇区
 46         MOV        BX,0
 47         MOV        DL,0x00           ; A驱动器
 48         INT        0x13              ; 调用磁盘BIOS
 49         JNC     next                 ; 没出错就跳转到next继续读下一个做准备
 50         ADD     SI,1                 ; SI++
 51         CMP     SI,5                 ; 比较SI和5
 52         JAE     error                ; SI>=5时,跳转到error
 53         MOV     AH,0x00
 54         MOV     DL,0x00              ; A驱动器
 55         INT     0x13                 ; 重置驱动器
 56         JMP     retry           
 57 next:   
 58         MOV     AX,ES                ; 把内存地址后移0x200
 59         ADD     AX,0x0020
 60         MOV     ES,AX                ; 因为没有ADD ES,0x200指令,所以这里绕个弯
 61         ADD     CL,1                 ; 往CL里加1 (所读扇区标号,开始是2,见初始化部分)
 62         CMP     CL,18                ; 和18比较 
 63         JBE     readloop             ; 小于18就跳转到readloop继续读 
 64         MOV     CL,1                 ; 扇区1
 65         ADD     DH,1                 ; 磁头+1
 66         CMP     DH,2                 ; 判断磁头是否超过2
 67         JB      readloop             ; 没有超过就继续读
 68         MOV     DH,0                 ; 超过2就转为0
 69         ADD     CH,1                 ; CH记录读取的柱面数
 70         CMP     CH,CYLS              ; CYLS在前面定义 CYLS EQU 10
 71         JB      readloop
 72 
 73 ; 磁盘内容装载内容的结束地址告诉haribote.sys
 74         MOV        [0x0ff0],CH       ; 将CYLS的值写到内存地址0x0ff0中 
 75         JMP        0xc200        
 76 
 77 error:
 78         MOV        SI,msg            ; 循环输出msg里面的内容     
 79         
 80 putloop:
 81         MOV        AL,[SI]
 82         ADD        SI,1              ; 给SI加1
 83         CMP        AL,0
 84         JE        fin
 85         MOV        AH,0x0e           ; 显示一个文字
 86         MOV        BX,15             ; 指定字符颜色
 87         INT        0x10              ; 调用显卡BIOS
 88         JMP        putloop                       
 89 fin:
 90         HLT                          ; 让CPU停止等待指令
 91         JMP        fin               ; 无限循环
 92         
 93 msg:
 94         DB        0x0a, 0x0a         ; 换行2次
 95         DB        "load error"
 96         DB        0x0a               ; 换行
 97         DB        0
 98 
 99         RESB    0x7dfe-$             ; 0x7dfe傑偱傪0x00偱杽傔傞柦椷
100 
101         DB        0x55, 0xaa
ipl10.asm(负责读取磁盘加载主程序)
  1 ; haribote-os boot asm
  2 ; TAB=4
  3 
  4 BOTPAK    EQU        0x00280000        ; bootpack偺儘乕僪愭
  5 DSKCAC    EQU        0x00100000        ; 僨傿僗僋僉儍僢僔儏偺応強
  6 DSKCAC0    EQU        0x00008000        ; 僨傿僗僋僉儍僢僔儏偺応強乮儕傾儖儌乕僪乯
  7 
  8 ; BOOT_INFO相关
  9 CYLS    EQU        0x0ff0            ; 设定启动区
 10 LEDS    EQU        0x0ff1
 11 VMODE    EQU        0x0ff2            ; 关于颜色的信息颜色的位数
 12 SCRNX    EQU        0x0ff4            ; 分辨率X
 13 SCRNY    EQU        0x0ff6            ; 分辨率Y
 14 VRAM    EQU        0x0ff8            ; 图像缓冲区的开始地址
 15 
 16         ORG        0xc200            ; 这个程序要装在到什么位置
 17 
 18         MOV        AL,0x13            ; VGA显卡,320X200X8位色彩
 19         MOV        AH,0x00
 20         INT        0x10
 21         MOV        BYTE [VMODE],8    ; 记录画面模式
 22         MOV        WORD [SCRNX],320
 23         MOV        WORD [SCRNY],200
 24         MOV        DWORD [VRAM],0x000a0000
 25 
 26 ; 用BIOS获得键盘上各种LED指示灯的状态
 27         MOV        AH,0x02
 28         INT        0x16             ; keyboard BIOS
 29         MOV        [LEDS],AL
 30 
 31 ; PIC偑堦愗偺妱傝崬傒傪庴偗晅偗側偄傛偆偵偡傞
 32 ;    AT屳姺婡偺巇條偱偼丄PIC偺弶婜壔傪偡傞側傜丄
 33 ;    偙偄偮傪CLI慜偵傗偭偰偍偐側偄偲丄偨傑偵僴儞僌傾僢僾偡傞
 34 ;    PIC偺弶婜壔偼偁偲偱傗傞
 35 
 36         MOV        AL,0xff
 37         OUT        0x21,AL
 38         NOP                        ; OUT柦椷傪楢懕偝偣傞偲偆傑偔偄偐側偄婡庬偑偁傞傜偟偄偺偱
 39         OUT        0xa1,AL
 40 
 41         CLI                        ; 偝傜偵CPU儗儀儖偱傕妱傝崬傒嬛巭
 42 
 43 ; CPU偐傜1MB埲忋偺儊儌儕偵傾僋僙僗偱偒傞傛偆偵丄A20GATE傪愝掕
 44 
 45         CALL    waitkbdout
 46         MOV        AL,0xd1
 47         OUT        0x64,AL
 48         CALL    waitkbdout
 49         MOV        AL,0xdf            ; enable A20
 50         OUT        0x60,AL
 51         CALL    waitkbdout
 52 
 53 ; 僾儘僥僋僩儌乕僪堏峴
 54 
 55 [INSTRSET "i486p"]                ; 486偺柦椷傑偱巊偄偨偄偲偄偆婰弎
 56 
 57         LGDT    [GDTR0]            ; 巄掕GDT傪愝掕
 58         MOV        EAX,CR0
 59         AND        EAX,0x7fffffff    ; bit31傪0偵偡傞乮儁乕僕儞僌嬛巭偺偨傔乯
 60         OR        EAX,0x00000001    ; bit0傪1偵偡傞乮僾儘僥僋僩儌乕僪堏峴偺偨傔乯
 61         MOV        CR0,EAX
 62         JMP        pipelineflush
 63 pipelineflush:
 64         MOV        AX,1*8            ;  撉傒彂偒壜擻僙僌儊儞僩32bit
 65         MOV        DS,AX
 66         MOV        ES,AX
 67         MOV        FS,AX
 68         MOV        GS,AX
 69         MOV        SS,AX
 70 
 71 ; bootpack偺揮憲
 72 
 73         MOV        ESI,bootpack    ; 揮憲尦
 74         MOV        EDI,BOTPAK        ; 揮憲愭
 75         MOV        ECX,512*1024/4
 76         CALL    memcpy
 77 
 78 ; 偮偄偱偵僨傿僗僋僨乕僞傕杮棃偺埵抲傊揮憲
 79 
 80 ; 傑偢偼僽乕僩僙僋僞偐傜
 81 
 82         MOV        ESI,0x7c00        ; 揮憲尦
 83         MOV        EDI,DSKCAC        ; 揮憲愭
 84         MOV        ECX,512/4
 85         CALL    memcpy
 86 
 87 ; 巆傝慡晹
 88 
 89         MOV        ESI,DSKCAC0+512    ; 揮憲尦
 90         MOV        EDI,DSKCAC+512    ; 揮憲愭
 91         MOV        ECX,0
 92         MOV        CL,BYTE [CYLS]
 93         IMUL    ECX,512*18*2/4    ; 僔儕儞僟悢偐傜僶僀僩悢/4偵曄姺
 94         SUB        ECX,512/4        ; IPL偺暘偩偗嵎偟堷偔
 95         CALL    memcpy
 96 
 97 ; asmhead偱偟側偗傟偽偄偗側偄偙偲偼慡晹偟廔傢偭偨偺偱丄
 98 ;    偁偲偼bootpack偵擟偣傞
 99 
100 ; bootpack偺婲摦
101 
102         MOV        EBX,BOTPAK
103         MOV        ECX,[EBX+16]
104         ADD        ECX,3            ; ECX += 3;
105         SHR        ECX,2            ; ECX /= 4;
106         JZ        skip            ; 揮憲偡傞傋偒傕偺偑側偄
107         MOV        ESI,[EBX+20]    ; 揮憲尦
108         ADD        ESI,EBX
109         MOV        EDI,[EBX+12]    ; 揮憲愭
110         CALL    memcpy
111 skip:
112         MOV        ESP,[EBX+12]    ; 僗僞僢僋弶婜抣
113         JMP        DWORD 2*8:0x0000001b
114 
115 waitkbdout:
116         IN         AL,0x64
117         AND         AL,0x02
118         JNZ        waitkbdout        ; AND偺寢壥偑0偱側偗傟偽waitkbdout傊
119         RET
120 
121 memcpy:
122         MOV        EAX,[ESI]
123         ADD        ESI,4
124         MOV        [EDI],EAX
125         ADD        EDI,4
126         SUB        ECX,1
127         JNZ        memcpy            ; 堷偒嶼偟偨寢壥偑0偱側偗傟偽memcpy傊
128         RET
129 ; memcpy偼傾僪儗僗僒僀僘僾儕僼傿僋僗傪擖傟朰傟側偗傟偽丄僗僩儕儞僌柦椷偱傕彂偗傞
130 
131         ALIGNB    16
132 GDT0:
133         RESB    8                ; 僰儖僙儗僋僞
134         DW        0xffff,0x0000,0x9200,0x00cf    ; 撉傒彂偒壜擻僙僌儊儞僩32bit
135         DW        0xffff,0x0000,0x9a28,0x0047    ; 幚峴壜擻僙僌儊儞僩32bit乮bootpack梡乯
136 
137         DW        0
138 GDTR0:
139         DW        8*3-1
140         DD        GDT0
141 
142         ALIGNB    16
143 bootpack:
asmhead.asm
1 void HariMain(void)
2 {
3 fin:
4     /* 这里想写上HLT,但是C语言中不能使用HLT! */
5     goto fin;
6 }
bootpack.c

下面说一下C语言部分:(以后为了启动操作系统还需要很多处理函数,所以就把这些处理函数打成一个包,为了好理解就取了bootpack名字),这里有c语言基础的人一眼就懂这个简单程序的意思了~关键是它如何变成机器语言的呢?下面就是详细过程:

  • 首先:使用cc1.exe从bootpack.c生成bootpack.gas[这一步主要实现将c语言编译为gas汇编]
  • 第二步:使用gas2nask.exe从bootpack.gas生成bootpack.nas[这一步主要实现从gas汇编到nas汇编]
  • 第三步:使用nask.exe从bootpack.nas生成bootpack.obj[将nas汇编编译为机器语言]
  • 第四步:使用bim2bim.exe从bootpack.obj生成bootpack.bim[制成二进制映像文件]
  • 第五步:使用bim2hrb.exe从bootpack.bim生成bootpack.hrb[将映像制成对应操作系统所需要的文件]
  • 最后:这样就做成了机器语言,再适用copy指令将asmhead.bin和bootpack.hrb单纯结合起来,就制成了haribote.sys[合并形成系统文件]

PS: 1) C语言编译生成的汇编不是nask汇编,所以要有1、2两步将c语言转换为nask汇编

   2) 我们用从C语言转换来的nask汇编然后转换成的机器语言是一种特殊的obj机器语言,必须与其他文件链接(link)之后才能形成真正可以运行的机器语言

   3) 这里我们把obj转换为bim映像其实就是将各个分部分链接在一起,做成一个完整的机器文件

   4) 此外,我们为了能让上面完整的机器语言实际使用,还要针对不同操作系统进行必要的加工,比如说加上识别文件头、压缩等

PS: 有人在windows和linux上也做过很多次C语言开发,但是也没有这么烦,说到底,是人家的编译器已经很成熟啦!我们为了要开发出适用不同平台的操作系统甚至是自己的操作系统,就没有将中间的过程省略~

PS: 这里的HariMain不要改动名字[因为程序要从这个地方开始运行,就像main函数的main]

>_<" 这里又要新建一个文件了:naskfunc.nas 它的功能就是用汇编写一些函数,供c语言调用(因为上面已经说过,32位模式下无法适用BIOS等功能),这个文件里主要写一些要用汇编实现的功能函数~

 1 ; naskfunc
 2 ; TAB=4
 3 
 4 [FORMAT "WCOFF"]                ; 制成目标文件的形式    
 5 [BITS 32]                        ; 制作32模式用的机器语言
 6 
 7 
 8 ; 制作目标文件信息
 9 
10 [FILE "naskfunc.nas"]            ; 源文件名信息
11 
12         GLOBAL    _io_hlt            ; 程序中包含的函数名
13 
14 
15 ; 以下是实际的函数
16 
17 [SECTION .text]        ; 目标文件中写了这些后再写程序
18 
19 _io_hlt:    ; void io_hlt(void);
20         HLT
21         RET

PS: 用汇编写的函数,之后还要与bootpack.obj链接,所以也需要编译成目标文件。因此将输出格式设置为WCOFF模式,另外还要设置为32位机器语言模式。在nask目标文件的模式下,必须设定文件名信息,然后再写明下面函数的名,注意这里的函数名前面要加”-“并且声明为GLOBAL。再往下就是实际的函数了,这里很简单~这里在bootpack.c要调用这个函数很简单,如下:

 1 /* 告诉c编译器,有一个函数在别的文件里 */
 2 
 3 void io_hlt(void);
 4 
 5 void HariMain(void)
 6 {
 7 
 8 fin:
 9     io_hlt(); /*执行naskfunc.nas里的_io_hlt*/
10     goto fin;
11 }

 

3.3 C语言实现内存写入

>_<" 上面已经实现了让画面黑屏,但是只做到这一点没意思,还是在画面上画点东西比较有趣。想要画东西的话,只要往VRAM里写点东西就可以了,但是c语言中又没有直接写入指定内存地址的语句(其实是有的,一步步来)。所以我们直接用汇编写一个函数,在naskfunc.nas里添加如下部分:

1 _write_mem8:    ; void write_mem8(int addr, int data);
2         MOV        ECX,[ESP+4]        ; [ESP+4]中存放的是地址,将其读入ECX
3         MOV        AL,[ESP+8]        ; [ESP+8]中存放的是数据,将其读入AL
4         MOV        [ECX],AL
5         RET
 1 ; naskfunc
 2 ; TAB=4
 3 
 4 [FORMAT "WCOFF"]                 ; 制成目标文件的形式   
 5 [INSTRSET "i486p"]                 ; 指明是486用的,EAX[新加的一条]
 6 [BITS 32]                        ; 制作32模式用的机器语言
 7 
 8 ; 制作目标文件信息
 9 [FILE "naskfunc.nas"]            ; 源文件名信息
10         GLOBAL    _io_hlt,_write_mem8     ;程序中包含的函数名
11 
12 ; 以下是实际的函数
13 [SECTION .text]        ; 目标文件中写了这些后再写程序
14 
15 _io_hlt:    ; void io_hlt(void);
16         HLT
17         RET
18 
19 _write_mem8:    ; void write_mem8(int addr, int data);
20         MOV        ECX,[ESP+4]        ; [ESP+4]中存放的是地址,将其读入ECX
21         MOV        AL,[ESP+8]        ; [ESP+8]中存放的是数据,将其读入AL
22         MOV        [ECX],AL
23         RET
naskfunc。nas全部代码[除了上面说的函数外,还有其他部分也做了修改]

上面的函数在c语言里调用write_mem8(0x1234,0x56);语句时,就相当于MOV BYTE[0x1234],0x56。如果C语言中调用了write_mem8函数,就会跳转到_write_mem8。此时参数指定的数字就是放在内存里,分别是:

  • 第一个数字的存放地址:[ESP+4]
  • 第二个数字的存放地址:[ESP+8]
  • 第三个数字的存放地址:[ESP+12]
  • 第四个数字的存放地址:[ESP+16]
  • (依次类推...)

那么在bootpack.c里面调用这个函数填写VRAM内存的方法如下:

 1 void io_hlt(void);
 2 void write_mem8(int addr, int data);
 3 
 4 void HariMain(void)
 5 {
 6     int i; /* 声明变量i,i是一个32位的整数 */
 7 
 8     for (i = 0xa0000; i <= 0xaffff; i++) { //遍历VRAM地址,每个写入15
 9         write_mem8(i, 15); /* MOV BYTE [i],15 */
10     }
11 
12     for (;;) {
13         io_hlt();
14     }
15 }

效果如下:整个屏幕变成了白色,这是因为向VRAM内都写入的15,意思是所有颜色都是第15个颜色,而第15个颜色正好是纯白色!

 

现在我们稍微改动一下,写入VRAM中的变量看看会有啥效果,我们将循环中的write_mem8(i,15)改成write_mem8(i,i & 0x0f),效果如下:我们这种写法就相当于每次写入i%16,所以每个16个像素,色号就反复一次,而整体呈现出如下的条纹图案:

 

3.4 C语言指针的强大

>_<" 上一节开头说过:c语言不能对内存进行读写,其实不是,c语言的指针能完美地完成内存读写的工作,这里就用c语言的指针代替上面用汇编写的write_mem8函数实现内存写入。

 1 void io_hlt(void);
 2 
 3 void HariMain(void)
 4 {
 5     int i; 
 6     char *p; /*用于BYTE型地址*/
 7 
 8     for (i = 0xa0000; i <= 0xaffff; i++) {
 9 
10         p = i; /* 带入地址 */
11         *p = (char*)i & 0x0f;
12 
13         /* 代替 write_mem8(i, i & 0x0f); */
14     }
15 
16     for (;;) {
17         io_hlt();
18     }
19 }

这样我们就完美的代替了汇编写的write_mem8函数,不得感叹一句指针真实强大(其实关于指针的很多细节,这里实在说不了了,但是它很强大,很强大~)!这里只说几点:

  • char *p;是BYTE类地址--short *p;是用于WORD类地址--int *p是用于DWORD类地址
  • 其实这样写就不用声明个p了:((char *)i)=i & 0x0f    ps:这种写法和BYTE[i]=i & 0x0f有些像吧
  • p表示地址,*p表示地址的内容
  • char *p是声明p,一般把*靠在p上是为了想声明2个指针时更清晰:char *p,*q;
  • *(p+i)也可以写成p[i]形式,但是这里p[i]不是数组,因为这里*(p+i)可以*(i+p)也行,即i[p]也对

 

3.5 色号设定与调色板

>_<" 我们想通过上面的内存写入可以画出一些乱的图案,但是我们想具体画一些东西的时候就要考虑颜色问题了。这里我们采用的是320X200的8位颜色模式,色号使用8位(二进制数),也就是只能从0~255的数。熟悉电脑颜色的人应该知道这是很少的,像电脑里一般用6位16进制的数,即24位(二进制数)来指定颜色。那么该怎样来指定颜色呢?

>_<" 这个8位色彩模式,是由程序员随意指定0~255的数字所对应的颜色。比如:25号对应#ffffff,26号对应#123456,如果像刚才那样程序员不做任何指定,0号就是#000000,15号就是#ffffff。这里我们想要制作个操作系统,因此由前辈的经验知道,只要用以下16种颜色就够了:

  • #000000:黑    #00ffff:浅亮色    #000084:暗蓝    #ff0000:亮红    #ffffff:白    #840084:暗紫    #00ff00:亮绿    #c6c6c6:亮灰
  • #008484:浅暗蓝   #ffff00:亮黄    #840000:暗红    #848484:暗灰    #0000ff:亮蓝    #008400:暗绿    #ff00ff:亮紫    #848400:暗黄

所以要对bootpack.c进行较大的修改:

 1 /*汇编里面的函数*/
 2 void io_hlt(void);
 3 void io_cli(void);   //汇编函数,禁止中断
 4 void io_out8(int port, int data);  //向指定的设备传送数据(第一参数是设备号,第二参数是传送数据)
 5 int io_load_eflags(void);   //用汇编写的函数,通过栈的处理获得当前中断标志
 6 void io_store_eflags(int eflags);  //用汇编写的函数,通过栈的处理设置当前中断标志
 7 
 8 /*同一个文件中的函数*/
 9 void init_palette(void);
10 void set_palette(int start, int end, unsigned char *rgb);
11 
12 void HariMain(void)
13 {
14     int i;
15     char *p; 
16 
17     init_palette(); /*设定调色板*/
18 
19     p = (char *) 0xa0000; /*指定地址*/
20 
21     for (i = 0; i <= 0xffff; i++) {
22         p[i] = i & 0x0f;
23     }
24 
25     for (;;) {
26         io_hlt();
27     }
28 }
29 
30 void init_palette(void)
31 {
32     static unsigned char table_rgb[16 * 3] = {
33         0x00, 0x00, 0x00,    /*  0:黑 */
34         0xff, 0x00, 0x00,    /*  1:亮红 */
35         0x00, 0xff, 0x00,    /*  2:亮绿 */
36         0xff, 0xff, 0x00,    /*  3:亮黄 */
37         0x00, 0x00, 0xff,    /*  4:亮蓝 */
38         0xff, 0x00, 0xff,    /*  5:亮紫 */
39         0x00, 0xff, 0xff,    /*  6:浅亮蓝 */
40         0xff, 0xff, 0xff,    /*  7:白 */
41         0xc6, 0xc6, 0xc6,    /*  8:亮灰 */
42         0x84, 0x00, 0x00,    /*  9:暗红 */
43         0x00, 0x84, 0x00,    /* 10:暗绿 */
44         0x84, 0x84, 0x00,    /* 11:暗黄 */
45         0x00, 0x00, 0x84,    /* 12:暗青 */
46         0x84, 0x00, 0x84,    /* 13:暗紫 */
47         0x00, 0x84, 0x84,    /* 14:浅暗蓝 */
48         0x84, 0x84, 0x84    /* 15:暗灰 */
49     };
50     set_palette(0, 15, table_rgb);
51     return;
52     /*C语言中static char只能用于数据,就像汇编中的DB指令 */
53 }
54 
55 void set_palette(int start, int end, unsigned char *rgb)
56 {
57     int i, eflags;
58     eflags = io_load_eflags();    /* 记录中断许可标志 */
59     io_cli();                     /* 将中断许可标志置0,禁止中断 */
60     io_out8(0x03c8, start);
61     for (i = start; i <= end; i++) {
62         io_out8(0x03c9, rgb[0] / 4);
63         io_out8(0x03c9, rgb[1] / 4);
64         io_out8(0x03c9, rgb[2] / 4);
65         rgb += 3;
66     }
67     io_store_eflags(eflags);    /* 恢复原中断 */
68     return;
69 }
新的bootpack.c
 1 ; naskfunc
 2 ; TAB=4
 3 
 4 [FORMAT "WCOFF"]                
 5 [INSTRSET "i486p"]                
 6 [BITS 32]                        
 7 [FILE "naskfunc.nas"]            
 8 
 9         GLOBAL    _io_hlt, _io_cli, _io_sti, _io_stihlt
10         GLOBAL    _io_in8,  _io_in16,  _io_in32
11         GLOBAL    _io_out8, _io_out16, _io_out32
12         GLOBAL    _io_load_eflags, _io_store_eflags
13 
14 [SECTION .text]
15 
16 _io_hlt:    ; void io_hlt(void);
17         HLT
18         RET
19 
20 _io_cli:    ; void io_cli(void);
21         CLI
22         RET
23 
24 _io_sti:    ; void io_sti(void);
25         STI
26         RET
27 
28 _io_stihlt:    ; void io_stihlt(void);
29         STI
30         HLT
31         RET
32 
33 _io_in8:    ; int io_in8(int port);
34         MOV        EDX,[ESP+4]        ; port
35         MOV        EAX,0
36         IN        AL,DX
37         RET
38 
39 _io_in16:    ; int io_in16(int port);
40         MOV        EDX,[ESP+4]        ; port
41         MOV        EAX,0
42         IN        AX,DX
43         RET
44 
45 _io_in32:    ; int io_in32(int port);
46         MOV        EDX,[ESP+4]        ; port
47         IN        EAX,DX
48         RET
49 
50 _io_out8:    ; void io_out8(int port, int data);
51         MOV        EDX,[ESP+4]        ; port
52         MOV        AL,[ESP+8]        ; data
53         OUT        DX,AL
54         RET
55 
56 _io_out16:    ; void io_out16(int port, int data);
57         MOV        EDX,[ESP+4]        ; port
58         MOV        EAX,[ESP+8]        ; data
59         OUT        DX,AX
60         RET
61 
62 _io_out32:    ; void io_out32(int port, int data);
63         MOV        EDX,[ESP+4]        ; port
64         MOV        EAX,[ESP+8]        ; data
65         OUT        DX,EAX
66         RET
67 
68 _io_load_eflags:    ; int io_load_eflags(void);
69         PUSHFD        ; 指 PUSH EFLAGS 
70         POP        EAX
71         RET
72 
73 _io_store_eflags:    ; void io_store_eflags(int eflags);
74         MOV        EAX,[ESP+4]
75         PUSH    EAX
76         POPFD        ; 指 POP EFLAGS 
77         RET
新的naskfunc.nas

PS: 这里要做几点说明(这篇文章说的太多了,不多说还说不清,早知道不把这么多放在一起了!)

1)访问调色板的步骤:(具体参照bootpack.c中的set_palette函数)

  • 屏蔽中断(CLI)
  • 将想要设定的调色板号写入0x03c8,紧接着按着R,G,B的顺序写入0x03c9,如果想继续设定下一个调色板,省略调色板号,继续按照RGB顺序写入0x03c9
  • 想要读出当前调色板的状态,首先要将调色板号写入0x03c7,再从0x03c9依次读3次,分别是R,G,B,如果想继续读取下一个,也是省略调色板号的设定继续读取
  • 如果最初执行了CLI那么要恢复中断STI

2)中断标志位的读取和设置要涉及到栈的应用,这里看一下代码就理解了(前提是你得懂栈这种数据结构)

3)在set_palette中要注意关闭中断与恢复中断,这个在操作系统中特别是嵌入式操作系统中应用较多

现在运行一下程序看看效果:[仔细看,和上面那个条纹图有点不一样吧]

 

3.6 简单界面实现

>_<" 上面我们已经把调色板配好了,接下来看看我们的绘画能力了,这里我们稍微改动一下bootpack.c里面的函数,实现画矩形的功能:

 1 void io_hlt(void);
 2 void io_cli(void);
 3 void io_out8(int port, int data);
 4 int io_load_eflags(void);
 5 void io_store_eflags(int eflags);
 6 
 7 void init_palette(void);
 8 void set_palette(int start, int end, unsigned char *rgb);
 9 void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
10 
11 #define COL8_000000        0
12 #define COL8_FF0000        1
13 #define COL8_00FF00        2
14 #define COL8_FFFF00        3
15 #define COL8_0000FF        4
16 #define COL8_FF00FF        5
17 #define COL8_00FFFF        6
18 #define COL8_FFFFFF        7
19 #define COL8_C6C6C6        8
20 #define COL8_840000        9
21 #define COL8_008400        10
22 #define COL8_848400        11
23 #define COL8_000084        12
24 #define COL8_840084        13
25 #define COL8_008484        14
26 #define COL8_848484        15
27 
28 void HariMain(void)
29 {
30     char *p; 
31 
32     init_palette(); 
33 
34     p = (char *) 0xa0000; 
35 
36     boxfill8(p, 320, COL8_FF0000,  20,  20, 120, 120);
37     boxfill8(p, 320, COL8_00FF00,  70,  50, 170, 150);
38     boxfill8(p, 320, COL8_0000FF, 120,  80, 220, 180);
39 
40     for (;;) {
41         io_hlt();
42     }
43 }
44 
45 void init_palette(void)
46 {
47     static unsigned char table_rgb[16 * 3] = {
48         0x00, 0x00, 0x00,    
49         0xff, 0x00, 0x00,    
50         0x00, 0xff, 0x00,    
51         0xff, 0xff, 0x00,    
52         0x00, 0x00, 0xff,    
53         0xff, 0x00, 0xff,    
54         0x00, 0xff, 0xff,    
55         0xff, 0xff, 0xff,    
56         0xc6, 0xc6, 0xc6,    
57         0x84, 0x00, 0x00,    
58         0x00, 0x84, 0x00,    
59         0x84, 0x84, 0x00,    
60         0x00, 0x00, 0x84,    
61         0x84, 0x00, 0x84,    
62         0x00, 0x84, 0x84,    
63         0x84, 0x84, 0x84    
64     };
65     set_palette(0, 15, table_rgb);
66     return;
67 }
68 
69 void set_palette(int start, int end, unsigned char *rgb)
70 {
71     int i, eflags;
72     eflags = io_load_eflags();    
73     io_cli();                 
74     io_out8(0x03c8, start);
75     for (i = start; i <= end; i++) {
76         io_out8(0x03c9, rgb[0] / 4);
77         io_out8(0x03c9, rgb[1] / 4);
78         io_out8(0x03c9, rgb[2] / 4);
79         rgb += 3;
80     }
81     io_store_eflags(eflags);
82     return;
83 }
84 
85 void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
86 {
87     int x, y;
88     for (y = y0; y <= y1; y++) {
89         for (x = x0; x <= x1; x++)
90             vram[y * xsize + x] = c;
91     }
92     return;
93 }
绘制矩形例子的bootpack.c

>_<" 进一步我们绘制一个像样点的窗口:[哈哈,有点那个意思了吧!]

  1 void io_hlt(void);
  2 void io_cli(void);
  3 void io_out8(int port, int data);
  4 int io_load_eflags(void);
  5 void io_store_eflags(int eflags);
  6 
  7 void init_palette(void);
  8 void set_palette(int start, int end, unsigned char *rgb);
  9 void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
 10 
 11 #define COL8_000000        0
 12 #define COL8_FF0000        1
 13 #define COL8_00FF00        2
 14 #define COL8_FFFF00        3
 15 #define COL8_0000FF        4
 16 #define COL8_FF00FF        5
 17 #define COL8_00FFFF        6
 18 #define COL8_FFFFFF        7
 19 #define COL8_C6C6C6        8
 20 #define COL8_840000        9
 21 #define COL8_008400        10
 22 #define COL8_848400        11
 23 #define COL8_000084        12
 24 #define COL8_840084        13
 25 #define COL8_008484        14
 26 #define COL8_848484        15
 27 
 28 void HariMain(void)
 29 {
 30     char *vram;
 31     int xsize, ysize;
 32 
 33     init_palette();
 34     vram = (char *) 0xa0000;
 35     xsize = 320;
 36     ysize = 200;
 37 
 38     boxfill8(vram, xsize, COL8_008484,  0,         0,          xsize -  1, ysize - 29);
 39     boxfill8(vram, xsize, COL8_C6C6C6,  0,         ysize - 28, xsize -  1, ysize - 28);
 40     boxfill8(vram, xsize, COL8_FFFFFF,  0,         ysize - 27, xsize -  1, ysize - 27);
 41     boxfill8(vram, xsize, COL8_C6C6C6,  0,         ysize - 26, xsize -  1, ysize -  1);
 42 
 43     boxfill8(vram, xsize, COL8_FFFFFF,  3,         ysize - 24, 59,         ysize - 24);
 44     boxfill8(vram, xsize, COL8_FFFFFF,  2,         ysize - 24,  2,         ysize -  4);
 45     boxfill8(vram, xsize, COL8_848484,  3,         ysize -  4, 59,         ysize -  4);
 46     boxfill8(vram, xsize, COL8_848484, 59,         ysize - 23, 59,         ysize -  5);
 47     boxfill8(vram, xsize, COL8_000000,  2,         ysize -  3, 59,         ysize -  3);
 48     boxfill8(vram, xsize, COL8_000000, 60,         ysize - 24, 60,         ysize -  3);
 49 
 50     boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 24, xsize -  4, ysize - 24);
 51     boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 23, xsize - 47, ysize -  4);
 52     boxfill8(vram, xsize, COL8_FFFFFF, xsize - 47, ysize -  3, xsize -  4, ysize -  3);
 53     boxfill8(vram, xsize, COL8_FFFFFF, xsize -  3, ysize - 24, xsize -  3, ysize -  3);
 54 
 55     for (;;) {
 56         io_hlt();
 57     }
 58 }
 59 
 60 void init_palette(void)
 61 {
 62     static unsigned char table_rgb[16 * 3] = {
 63         0x00, 0x00, 0x00,    
 64         0xff, 0x00, 0x00,    
 65         0x00, 0xff, 0x00,    
 66         0xff, 0xff, 0x00,    
 67         0x00, 0x00, 0xff,    
 68         0xff, 0x00, 0xff,    
 69         0x00, 0xff, 0xff,    
 70         0xff, 0xff, 0xff,    
 71         0xc6, 0xc6, 0xc6,    
 72         0x84, 0x00, 0x00,    
 73         0x00, 0x84, 0x00,    
 74         0x84, 0x84, 0x00,    
 75         0x00, 0x00, 0x84,    
 76         0x84, 0x00, 0x84,    
 77         0x00, 0x84, 0x84,    
 78         0x84, 0x84, 0x84    
 79     };
 80     set_palette(0, 15, table_rgb);
 81     return;
 82 }
 83 
 84 void set_palette(int start, int end, unsigned char *rgb)
 85 {
 86     int i, eflags;
 87     eflags = io_load_eflags();    
 88     io_cli();                 
 89     io_out8(0x03c8, start);
 90     for (i = start; i <= end; i++) {
 91         io_out8(0x03c9, rgb[0] / 4);
 92         io_out8(0x03c9, rgb[1] / 4);
 93         io_out8(0x03c9, rgb[2] / 4);
 94         rgb += 3;
 95     }
 96     io_store_eflags(eflags);
 97     return;
 98 }
 99 
100 void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
101 {
102     int x, y;
103     for (y = y0; y <= y1; y++) {
104         for (x = x0; x <= x1; x++)
105             vram[y * xsize + x] = c;
106     }
107     return;
108 }
绘制一个窗口的bootpack.c文件
 
 
LZ说明:没想到一口气竟然说了这么多,总结一下,其实就揭示了一个过程:编译器是如何从c等高级语言转换为机器语言然后控制硬件呈现效果的~里面涉及的知识略多,有些确实不能用几句话说完~明天就要上课了,今天就到这里吧~(http://www.cnblogs.com/zjutlitao/)
 
 
 

 

posted @ 2014-09-01 02:49  beautifulzzzz  阅读(6712)  评论(19编辑  收藏  举报