最近,我在学习着日本作家川合秀实30天制作操作系统的书。看到书上的内容,我就想要自己也仿照其中的代码,将其改编为流行的编译器可以编译和运行的代码。
写操作系统代码,需要用C语言与汇编语言。C语言方面,一般地,大家都是选择用GCC编译器来编译C语言。在汇编语言方面,一般地,大家都是选择用nasm汇编语言。
我这次改编川合的代码,并未采用nasm的语法,而是采用了GCC所支持的GNU AS语法,也就是用了AT&T汇编语言。
之所以选择用AT&T汇编语言,那是因为,Linux内核,本来就是用AT&T汇编语言写的。我觉得,若是采用AT&T汇编语言的话,可以很方便地去阅读现有的Linux内核。
而对于nasm汇编语言,我觉得,需要了解其语法,因为,许多的写微型内核的作者,许多国内的学习与开发者,都是在用nasm汇编语言的。
我呢,我觉得,我只是需要去了解nasm的语法,并不想在这里耗费太多的精力。
为了学习GNU AS汇编,为了学习内核的知识,我还需要将流行的微型内核的nasm代码,改编为GNU AS语法的。
在这样的一种改编之中,我自己,算是学到了好多的知识。
目前,算是正在学习中。我的学习,还尚未成型。我想要去改编Linux 0.12内核,结果呢,里面有一些bug,我至今未能够解决。
因为原版的0.12内核我没有能够成功地改编,所以就想要去试着改编流行的微型内核。希望在学习流行的微型内核的过程中,逐渐地积累内核的知识。
目前,我是在尝试着去改编川合先生的代码。
在改编的过程中,我发现,川合先生的代码是有点坑人啊。里面的一些个代码,用川合先生自己开发的编译器,是能够编译的。而用流行的编译器去编译的时候,就不行了。
比如说,川合的书上有一个 jmp 0xC200的代码。这样的代码,其实在英特尔汇编里面,是没有这样的语法的。而川合的书里面出现了这样的语法。这样的东西,那就是只适合川合先生自己设计的编译器。汇编入门教材,王爽老师的16位汇编里面,也不支持这样的语法的。
再比如,在CPU从实模式切换为保护模式的时候,需要一个跳转指令来刷新流水线。此时,在流行的汇编语言里面,它需要的是一条远跳转指令。
这样的远跳转指令,用nasm来描述的话,它的格式如下。
jmp dword segment_selector:offset
而在川合先生的书里面,他用的是一个短跳转指令,jmp后面直接跟的是offset的地址标号。
这样的坑点,还真的是有啊。
不管是在川合先生的书里面,还是在国内的教材的学习当中,我遇到的坑点,都是不少的。
最近,我在改编川合先生的代码的时候,在进行到了显示字符与字符串的课节的时候。川合先生说,想要显示全部的ASCII码字符,需要编译作者提供的hankaku.txt文件。编译的时候,需要用到作者那里提供的一些个编编译软件。
作者提供的东西,在Windows里面可用。Linux里面,是不能够直接作者提供的Windows的工具软件的。作者的网站里面,其实是提供了Linux系统下面的工具软件的。不过啊,人家的官网用的是日语啊,我看不懂啊。所以呢,有这种好用的Linux软件,我也用不了。
为了能够在改编的内核里面显示字符与字符串,我就得是自己来想办法了。
咋办呢?我看了看作者给出的hankaku.txt文件,里面呢,大部分内容都是256个ASCII码字符的十六进制数据,并且,这些个重要数据,与作者的说明的内容并不混淆。作者在说明部分,没有用到英文句号与星号,而这个字体文件,在描述的时候,全都用的是英文句号与星号。
想来想去,我是决定着,自己来编写程序,将作者的这个hankaku.txt文件,转码输出为C语言头文件。
在头文件里面,声明一个大的数组,用来包含全部的ASCII码字符的字体数据。
按照作者川合先生的设计,每一个字符用16字节的数字来表示,一共有256个字符,16乘以256,等于4096.这样,声明一个4096字节的数组,每一个元素为unsigned char类型的,应该就可以了。
在选择程序语言的时候,我可以选择的语言有两种。第一种是C语言,第二种是C++。C语言,我手边没有书,所以,起初,我是想要采用C++的。我去查找手边的谭浩强教授的C++教材,结果发现,用不了。里面介绍的一些个语法知识是错误的。还有一些个语法,书上还没有介绍,我的要求就没有达到。
没办法啊,C++用不了,我就用c语言来写程序呗。
其实C语言挺好的,虽说只是面向过程的语言,但是呢,它的语法,简洁,流畅,又功能强大,实在是一个很好的编程语言。
虽说C语言很好,可是,我手边没有书啊,好多的语法,我都给忘记了。所以呢,我还得是去网上找相关的语法讲解。
讲解是找到了。我照着网上的语法讲解,来设计程序代码。经过几次调试,算是成功地写出了能够转换hankaku,txt的编码的程序。我将这个程序,命名为make_font.exe。源代码,我将其命名为make_font.c。
这是一个典型的C语言文件。
接下来,我将源码贴在这里,欢迎有需要的网友下载和使用。
#include <stdio.h>
#include <stdlib.h>
int a[8] = {128, 64, 32, 16, 8, 4, 2, 1};
int main(void)
{
FILE * fp_in, *fp_out;
char ch;
int i, j;
int n;
char infile_name[60] = "hankaku.txt";
char outfile_name[60] = "hankaku.h";
i = 0;
j = 0;
n = 0;
fp_in = fopen(infile_name, "r");
fp_out = fopen(outfile_name, "w+");
if (!fp_in || !fp_out)
{
printf("open file fail.\n\n");
exit(0);
}
else
{
printf("open file successfully.\n\n");
}
fprintf(fp_out, "#ifndef _HANKAKU_H_\n");
fprintf(fp_out, "#define _HANKAKU_H_\n\n");
fprintf(fp_out, "unsigned char hankaku[4096] = {\n");
while ((ch = fgetc(fp_in)) != EOF)
{
if (ch == '.')
{
n += a[i] * 0;
i++;
}
else if (ch == '*')
{
n += a[i] * 1;
i++;
}
else
{
continue;
}
if (i >= 8)
{
fprintf(fp_out, "0x%X", n);
n = 0;
i = 0;
j++;
}
else
{
continue;
}
if (j >= 4096)
{
printf("this task is done.\n");
printf("happy every day.\n\n");
break;
}
else if (j % 16 == 0)
{
fprintf(fp_out, ",\n\n");
}
else if (j % 4 == 0)
{
fprintf(fp_out, ",\n");
}
else
{
fprintf(fp_out, ",\t");
}
}
fprintf(fp_out, "\n};\n\n");
fprintf(fp_out, "#endif\n\n\n");
fclose(fp_in);
fclose(fp_out);
printf("task is ok.\n\n");
if (j < 4096)
{
printf("this is a test file, not a real task.\n\n");
printf("You can pratice it next time.");
}
else
{
printf("this is a real task.\n");
printf("good job.\n");
printf("Hope you success.\n\n\n");
}
return 0;
}
全部的代码都在上面了。
作者提供的字体库文件名为hankaku.txt,我自己在转码这个文件的时候,将目标文件命名为hankaku.h。
命名的目标文件的名字,代码里面有。
char infile_name[60] = "hankaku.txt";
char outfile_name[60] = "hankaku.h";
在代码文件的开头,有这样的两行代码。上面的一行代码,是输入文件的名字,也就是要被转换编码的文件,是作者的那个hankaku.txt。而下面的一行代码,写了输出文件的名字,hankaku.h。
hankaku.h是一个C语言头文件。
运行了程序以后,hankaku.h的开头部分是下面这样的。
#ifndef _HANKAKU_H_
#define _HANKAKU_H_
unsigned char hankaku[4096] = {
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x38, 0x44,
0x82, 0xAA, 0xAA, 0x82,
0x82, 0xAA, 0x92, 0x44,
0x38, 0x0, 0x0, 0x0,
结尾部分,是下面这样的。
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0
};
#endif
从输出文件上,大家可以看到,在输出代码里,我是声明了一个大数组.unsigned char hankaku[4096]。在输出的格式调整上,我让每一行放置4个数字,每一个数字,都是16进制的。每当输出了4个数,就换行。
由于一个字符占用着16个字节,因此,每当输出了16个数字以后,我就插入两个换行。这样,每一个字符的数字编码,就很容易看清楚了。
最后呢,所有的元素都输出完毕以后,写上大括号的右半部分。
大括号后面,还得加一个分号。这个分号,我就曾经给忘记加了。写这个程序的时候,我才想起来,数组声明了以后,是要使用分号的。
数组,结构体,声明完了之后,是否需要加分号,这样的零散知识,我是容易搞错的。
最后呢,写上#endif。
这个代码,就是我自己写的制作字体的程序了。
目前,我只是改编到作者的第五章,显示字符串。再往后,就还没有改编。
后面,还会有显示文字的程序代码。作者原书是说,要显示日文。而国人在翻译的时候,将其转换为了显示中文。
还不知道,到时候,这个显示中文的程序,我能否成功地改编出来。但愿,这个显示中文的部分,里面的字库文件的转换工作,不是很难。
现在,我还没有进行到那里,原书,我也没有学习到显示中文或日文的章节。
看情况了,若是到时候,我能够成功地改编作者的程序,将中文字库给转换出来,并且在川合先生开发的系统里面,成功地显示了中文字符,到时候,我再来发文帖我的代码好了。
这次的代码,就先贴这些了。
我还想说一说,作者的那个进入保护模式的部分的汇编代码,用现有的编译器,它编译不了啊。
作者提供的描述符是有问题的,不符合英特尔处理器的描述符格式。而这样的代码,它居然能够成功地编译,这是说明,作者提供的自制的编译器,或者是提供的qemu配套软件,是作出了特殊的处理。
学习的时候,我是觉得,操作系统内核的学习,实在是坑多啊。
也不知道,后面又会踩上多少坑。现在阶段的学习,难度上,还是很大的。
只好是慢慢地来学习了。学习编程,也许,就是需要去填平很多的坑点了。