「30天自制操作系统」Day29 - 压缩与简单的应用程序

文件压缩

这里作者使用了一个叫做 tek 的格式,这里在 tek.c 文件中加入了两个函数:tek_getsize 和 tek_decomp。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int tek_getsize(unsigned char *p)
{
static char header[15] = {
0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x4f, 0x53, 0x41, 0x53, 0x4b, 0x43, 0x4d, 0x50
};
int size = -1;
if (memcmp(p + 1, header, 15) == 0 && (*p == 0x83 || *p == 0x85 || *p == 0x89)) {
p += 16;
size = tek_getnum_s7s(&p);
}
return size;
}

int tek_decomp(unsigned char *p, char *q, int size)
{
int err = -1;
if (*p == 0x83) {
err = tek_decode1(size, p, q);
} else if (*p == 0x85) {
err = tek_decode2(size, p, q);
} else if (*p == 0x89) {
err = tek_decode5(size, p, q);
}
if (err != 0) {
return -1; /* 失败 */
}
return 0; /* 成功 */
}

tek_getsize 的作用是判断文件是否符合 tek 格式,如果合法的 tek 格式则取得解压缩后的文件大小。tek_decomp 的作用是完成解压缩操作。

利用这两个函数,我们编写了 file_loadfile2,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
char *file_loadfile2(int clustno, int *psize, int *fat)
{
int size = *psize, size2;
struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
char *buf, *buf2;
buf = (char *) memman_alloc_4k(memman, size);
file_loadfile(clustno, size, buf, fat, (char *) (ADR_DISKIMG + 0x003e00));
if (size >= 17) {
size2 = tek_getsize(buf);
if (size2 > 0) { /* 使用 tek 格式压缩的文件 */
buf2 = (char *) memman_alloc_4k(memman, size2);
tek_decomp(buf, buf2, size2);
memman_free_4k(memman, (int) buf, size);
buf = buf2;
*psize = size2;
}
}
return buf;
}

这个函数的功能是,首先用申请必要的内存空间,然后用 file_loadfile 函数将文件内容载入内存。如果文件大小超过 17 字节则调用 tek_getsize 进行判断,如果确实为 tek 格式则为解压缩后的文件申请分配内存空间,并执行解压缩操作,然后舍弃解压缩前的文件内容。本函数将返回载入并存放文件内容的内存地址。

我们使用如下命令即可完成文件的压缩:

1
> bim2bin -osacmp in:nihongo.org out:nihongo.fnt

要想让应用程序经过 tek 压缩后也可以直接运行,我们需要修改 cmd_app,如下所示:

1
2
3
4
if (finfo != 0) {
appsiz = finfo->size;
p = file_loadfile2(finfo->clustno, &appsiz, fat);
}

标准函数

接下来我们来实现一些标准函数。

首先是 putchar,这个直接调用 api_putchar 就可以了。

然后是 strcmp,编译器已经附带了。

用来结束应用程序的 exit,可以通过调用 api_end 来实现。

printf 可以通过调用 sprintf 来实现,注意要使用 C 语言的可变参数。

malloc 和 free 函数,只要调用 api_malloc 和 api_free 函数就可以实现了。需要注意的是标准函数的 free 我们不能指定 size,因此我们在 malloc 的时候多分配了 16 个字节的空间,将 size 存放在此处,在 free 的时候执行相反的操作。

非矩形窗口

所谓非矩形,我们可以通过使用透明色来实现,用透明色在窗口中绘制几个方块就可以了。效果如下图所示:

bball

我们通过绘制直线来绘制一个球,注意我们要保证 refresh 范围指定正确的左上角和右下角坐标。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void HariMain(void)
{
int win, i, j, dis;
char buf[216 * 237];
struct POINT {
int x, y;
};
static struct POINT table[16] = {
{ 204, 129 }, { 195, 90 }, { 172, 58 }, { 137, 38 }, { 98, 34 },
{ 61, 46 }, { 31, 73 }, { 15, 110 }, { 15, 148 }, { 31, 185 },
{ 61, 212 }, { 98, 224 }, { 137, 220 }, { 172, 200 }, { 195, 168 },
{ 204, 129 }
};

win = api_openwin(buf, 216, 237, -1, "bball");
api_boxfilwin(win, 8, 29, 207, 228, 0);
for (i = 0; i <= 14; i++) {
for (j = i + 1; j <= 15; j++) {
dis = j - i;
if (dis >= 8) {
dis = 15 - dis;
}
if (dis != 0) {
api_linewin(win, table[i].x, table[i].y, table[j].x, table[j].y, 8 - dis);
}
}
}

for (;;) {
if (api_getkey(1) == 0x0a) {
break;
}
}
api_end();
}

看一下画出来的效果吧!

外星人游戏

程序很长,这里就不放了。不过读完一遍程序下来,深深地感受到了 API 的重要性。我们之前费工夫设计了那么多 API,比如操作窗口的、操作定时器的、输出字符串的等等,使我们的开发速度得到了的很大的提升。游戏的效果图如下: