代码仓库地址:https://github.com/freedom-xiao007/operating-system
简介
在上篇中我们成功的加载跳转执行了C语言的代码,本篇中将跟随书籍,初步展示了一个系统页面的初步界面,看到桌面那刻还是有点意思的
最终结果展示
不多啰嗦,这里先直接展示结果,最终的运行界面如下:
可以看到有点模样了,令人激动啊。当最终到这一步的时候,心里开心极了,哈哈
相关代码说明
本篇中的代码修改基本上是抄《30天自制操作系统》中的,但可能是nask和nasm的区别问题,我们需要单独修改的地方也不少,下面我们详细说明下需要修改的地方
setup.asm文件修改
这个文件需要添加界面相关的参数设置,我们将其插入到start的下面,如下:
start:
; 画面モードを設定
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
func.asm文件修改
这次新增了比较多的函数,但不复杂,也不多,这个文件的内容如下:
[BITS 32] ; 制作32位模式用的机器语言
GLOBAL _io_hlt, _io_cli, _io_sti, io_stihlt
GLOBAL _io_in8, _io_in16, _io_in32
GLOBAL _io_out8, _io_out16, _io_out32
GLOBAL _io_load_eflags, _io_store_eflags
[SECTION .text]
_io_hlt: ; void io_hlt(void);
HLT
RET
_io_cli: ; void io_cli(void);
CLI
RET
_io_sti: ; void io_sti(void);
STI
RET
_io_stihlt: ; void io_stihlt(void);
STI
HLT
RET
_io_in8: ; int io_in8(int port);
MOV EDX, [ESP+4] ; port
MOV EAX,0
IN AL, DX
RET
_io_in16: ; int io_in16(int port);
MOV EDX, [ESP+4] ; port
MOV EAX,0
IN AX, DX
RET
_io_in32: ; int io_in32(int port);
MOV EDX, [ESP+4] ; port
IN EAX, DX
RET
_io_out8: ; void io_out8(int port, int data);
MOV EDX, [ESP+4] ; port
MOV AL, [ESP+8] ; data
OUT DX, AL
RET
_io_out16: ; void io_out16(int port, int data);
MOV EDX, [ESP+4] ; port
MOV EAX, [ESP+8] ; data
OUT DX, AX
RET
_io_out32: ; void io_out32(int port, int data);
MOV EDX, [ESP+4] ; port
MOV EAX, [ESP+8] ; data
OUT DX, EAX
RET
_io_load_eflags: ; int io_load_eflags(void);
PUSHFD ; 指PUSH EFLAGS
POP EAX
RET
_io_store_eflags: ; void io_store_eflags(int eflags);
MOV EAX, [ESP+4]
PUSH EAX
POPFD ; 指POP EFLAGS
RET
c入口文件修改
本次直接使用书中的C相关代码,但有几处不适配,需要我们单独进行修改
- 第一处是原书中的boxfill8函数,用于绘制矩形的:原书中是选循环y再循环x,但在博主运行时,总是会多出一个memset函数,导致运行失败,系统不断重启
需要将其修改下,替换为先循环x再循环y(不断地尝试后发现的,但没有搞懂为啥会这样)
- 第二处是调用boxfill8时的线条高度问题:原书中下发的两个矩形都是线条出度为1左右(我猜测),但在博主尝试中,小于3的高度不会显示
这里就需要修改下传入boxfill8的y0和y1的差值最小为3
经过修改后,文件的所有内容如下:
#define COL8_000000 0
#define COL8_FF0000 1
#define COL8_00FF00 2
#define COL8_FFFF00 3
#define COL8_0000FF 4
#define COL8_FF00FF 5
#define COL8_00FFFF 6
#define COL8_FFFFFF 7
#define COL8_C6C6C6 8
#define COL8_840000 9
#define COL8_008400 10
#define COL8_848400 11
#define COL8_000084 12
#define COL8_840084 13
#define COL8_008484 14
#define COL8_848484 15
/*就算写在同一个源文件里,如果想在定义前使用,还是必须事先声明一下。*/
void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);
void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
void start(void)
{
init_palette(); /* 设定调色板 */
char *vram; /* 变量p是BYTE [...]用的地址 */
vram = (char *) 0xa0000; /* 指定地址 */
int xsize = 320;
int ysize = 200;
boxfill8(vram, xsize, COL8_008484, 0, 0, xsize, ysize - 29);
boxfill8(vram, xsize, COL8_C6C6C6, 0, ysize - 28, xsize, ysize - 28);
boxfill8(vram, xsize, COL8_FFFFFF, 0, ysize - 27, xsize, ysize - 27);
boxfill8(vram, xsize, COL8_C6C6C6, 0, ysize - 26, xsize, ysize - 1);
// y距离有时候需要大于3
boxfill8(vram, xsize, COL8_FFFFFF, 3, ysize - 24, 59, ysize - 21);
boxfill8(vram, xsize, COL8_FFFFFF, 2, ysize - 24, 2, ysize - 4);
boxfill8(vram, xsize, COL8_848484, 3, ysize - 4, 59, ysize - 1);
boxfill8(vram, xsize, COL8_848484, 59, ysize - 23, 59, ysize - 5);
boxfill8(vram, xsize, COL8_000000, 2, ysize - 3, 59, ysize - 0);
boxfill8(vram, xsize, COL8_000000, 60, ysize - 24, 60, ysize - 3);
boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 24, xsize - 4, ysize - 21);
boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 23, xsize - 47, ysize - 4);
boxfill8(vram, xsize, COL8_FFFFFF, xsize - 47, ysize - 3, xsize - 4, ysize - 0);
boxfill8(vram, xsize, COL8_FFFFFF, xsize - 3, ysize - 24, xsize - 3, ysize - 3);
for (; ; ) {
io_hlt();
}
}
void init_palette(void)
{
static unsigned char table_rgb[16 * 3] = {
0x00, 0x00, 0x00, /* 0:黑 */
0xff, 0x00, 0x00, /* 1:亮红 */
0x00, 0xff, 0x00, /* 2:亮绿 */
0xff, 0xff, 0x00, /* 3:亮黄 */
0x00, 0x00, 0xff, /* 4:亮蓝 */
0xff, 0x00, 0xff, /* 5:亮紫 */
0x00, 0xff, 0xff, /* 6:浅亮蓝 */
0xff, 0xff, 0xff, /* 7:白 */
0xc6, 0xc6, 0xc6, /* 8:亮灰 */
0x84, 0x00, 0x00, /* 9:暗红 */
0x00, 0x84, 0x00, /* 10:暗绿 */
0x84, 0x84, 0x00, /* 11:暗黄 */
0x00, 0x00, 0x84, /* 12:暗青 */
0x84, 0x00, 0x84, /* 13:暗紫 */
0x00, 0x84, 0x84, /* 14:浅暗蓝 */
0x84, 0x84, 0x84 /* 15:暗灰 */
};
set_palette(0, 15, table_rgb);
return;
/* C语言中的static char语句只能用于数据,相当于汇编中的DB指令 */
}
void set_palette(int start, int end, unsigned char *rgb)
{
int i, eflags;
eflags = io_load_eflags(); /* 记录中断许可标志的值*/
io_cli(); /* 将中断许可标志置为0,禁止中断 */
io_out8(0x03c8, start);
for (i = start; i <= end; i++) {
io_out8(0x03c9, rgb[0] / 4);
io_out8(0x03c9, rgb[1] / 4);
io_out8(0x03c9, rgb[2] / 4);
rgb += 3;
}
io_store_eflags(eflags); /* 复原中断许可标志 */
return;
}
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
int x, y;
for (x = x0; x <= x1; x++) {
for (y = y0; y <= y1; y++) {
vram[y * xsize + x] = c;
}
}
return;
}
脚本还是使用原来的,本篇中没有变化,一键运行后,出现文章开头令人激动的画面
初步感受C代码转汇编
在书中开始时介绍了不同手段实现同一个效果:给内存赋值
第一个方式是使用wirte_mem8时的编译nasm代码:使用写汇编函数的方式,根据传入的值,写到相应的内存中
void write_mem8(int addr, int data);
void start(void) {
int i;
for (i = 0xa0000; i < 0xaffff; i++) {
write_mem8(i, i & 0x0f);
}
}
ALIGN 8
?_001: mov eax, ebx ; 0018 _ 89. D8
mov dword [esp], ebx ; 001A _ 89. 1C 24
add ebx, 1 ; 001D _ 83. C3, 01
and eax, 0FH ; 0020 _ 83. E0, 0F
mov dword [esp+4H], eax ; 0023 _ 89. 44 24, 04
call _write_mem8 ; 0027 _ E8, 00000000(rel)
cmp ebx, 720895 ; 002C _ 81. FB, 000AFFFF
jnz ?_001 ; 0032 _ 75, E4
_write_mem8: ; void write_mem8(int addr, int data);
MOV ECX, [ESP+4] ; [ESP + 4]中存放的是地址,将其读入ECX
MOV AL, [ESP+8] ; [ESP + 8]中存放的是数据,将其读入AL
MOV [ECX], AL
RET
第二个是指针,使用char*时的编译nasm代码:突然有种回到以前学习C的感觉,想到初期使用指针时的痛苦
void start(void) {
int i;
char *p;
for (i = 0xa0000; i < 0xaffff; i++) {
p = (char *) i;
*p = i & 0x0f;
}
/* 或者下面这样写,生成的汇编码也一样 */
char *p;
p = (char *) 0xa0000; /*将地址赋值进去*/
for (i = 0; i <= 0xffff; i++) {
p[i] = i & 0x0f;
}
}
?_001: mov edx, eax ; 0018 _ 89. C2
add eax, 1 ; 001A _ 83. C0, 01
and edx, 0FH ; 001D _ 83. E2, 0F
mov byte [eax-1H], dl ; 0020 _ 88. 50, FF
cmp eax, 720895 ; 0023 _ 3D, 000AFFFF
jnz ?_001 ; 0028 _ 75, EE
可以看到两种不同的实现方式,对C的指针有了更深的理解,了解底层确实是有一定帮助的
总结
本文相对而言还是比较顺利的,虽然有一点坎坷,但在不断的调试中,顺利解决了
还进一步理解了C的指针
但关于书中的控制屏幕显示部分,目前还不是很通透,很需要再刷刷