自制操作系统日志——第二十九、三十天
其实,在昨天我们已经完成了我们这个纸娃娃操作系统的基本功能了。但是呢,我们今天还是要继续进一步的完善一下,那么今天呢,主要就是进行一下压缩与制作一些简单的小游戏。
一、压缩
这里呢,我们想要给操作系统添加上一个功能。即,我们可以支持文件压缩的功能。当然,这一个支持文件压缩的功能并不是说我们制作一个用于压缩的程序,而是说我们能够将压缩好的文件可以在操作系统内部进行解压缩,即对于压缩过的文件,我们一九可以做到像未经压缩的文件一样使用。
为什么要这样子做呢?其实主要还是因为,我们想要整个操作系统读入的文件大小更加小巧而已!! 那么接下来就让我们开始制作吧:
对于解压缩的文件tek.c 这里我不在赘述了,直接将源代码中的tek.c放进haribote文件夹里。以及在添加上tek的文件夹即可。然后,我们添加导入的函数:
- file.c: file_loadfile2 ==> 函数的功能是,首先使用alloc申请必要的内存空间;然后用file_loadfile将文件内容导入内存空间; 如果文件大小超过17KB则有可能是tek的文件,则调用tek_getsize进行判断,如果判断该文件确实是tek文件,则为解压缩后的文件申请内存空间,并执行解压缩操作,并舍弃之前的文件内容,函数返回存放文件的内存地址:
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;
}
- graphic.c: 修改之前的导入字库文件:
void font_init(unsigned char mode)
{
struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
int *fat;
int i;
unsigned char *nihongo, *hzk;
struct FILEINFO *finfo;
extern char hankaku[4096];
if( mode == 3)
{
//载入HZK16,中文字符集
fat = (int *) memman_alloc_4k(memman, 4 * 2880);
file_readfat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200));
finfo = file_search("hzk16s.fnt", (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
if (finfo != 0) {
i = finfo->size;
hzk = file_loadfile2(finfo->clustno, &i, fat);
} else {
hzk = (unsigned char *) memman_alloc_4k(memman, 0x5d5d * 32);
for (i = 0; i < 16 * 256; i++) {
hzk[i] = hankaku[i]; /* 没有字符库,则直接赋值前面的英文字符库 */
}
for (i = 16 * 256; i < 0x5d5d * 32; i++) {
hzk[i] = 0xff; /* 剩下部分填充0xff*/
}
}
*((int *) 0x0fe8) = (int) hzk; //用0xfe8存字库地址
memman_free_4k(memman, (int) fat, 4 * 2880);
}
if( mode == 2)
{
fat = (int *) memman_alloc_4k(memman, 4 * 2880);
file_readfat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200));
finfo = file_search("nihongo.fnt", (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
if (finfo != 0) {
i = finfo->size;
nihongo = file_loadfile2(finfo->clustno, &i, fat);
} else {
for (i = 0; i < 16 * 256; i++) {
nihongo = (unsigned char *) memman_alloc_4k(memman, 16 * 256 + 32 * 94 * 47);
nihongo[i] = hankaku[i]; /* 没有字符库,则直接赋值前面的英文字符库 */
}
for (i = 16 * 256; i < 16 * 256 + 32 * 94 * 47; i++) {
nihongo[i] = 0xff; /* 剩下部分填充0xff*/
}
}
*((int *) 0x0fd8) = (int) nihongo; //用0xfe8存字库地址
memman_free_4k(memman, (int) fat, 4 * 2880);
}
}
然后,我们继续加把劲,压缩一下其他的应用程序:
console.c:
int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
{
略
int i, segsiz, datsiz, esp, dathrb, appsiz;
略
if (finfo != 0) {
/*找到文件的情况*/
appsiz = finfo->size;
p = file_loadfile2(finfo->clustno, &appsiz, fat);
if (appsiz >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
略
}
略
memman_free_4k(memman, (int) p, appsiz);
cons_newline(cons);
return 1;
}
int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{
略
else if (edx == 21){//打开文件
for(i = 0; i < 8; i++){
if(task->fhandle[i].buf == 0){
break;
}
}
fh = &task->fhandle[i];
reg[7] = 0;
if(i < 8){
finfo = file_search((char *) ebx + ds_base,
(struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
if(finfo != 0){
reg[7] = (int) fh;//返回文件句柄
fh->buf = (char *) memman_alloc_4k (memman, finfo->size);
fh->size = finfo->size;
fh->pos = 0;
fh->buf = file_loadfile2(finfo->clustno, &fh->size, task->fat);
}
}
}
略
}
然后,我们还需要修改一下,app_make.txt:
%.org : %.bim Makefile ../app_make.txt
$(BIM2HRB) $*.bim $*.org $(MALLOC)
%.hrb : %.org Makefile ../app_make.txt
$(BIM2BIN) -osacmp in:$*.org out:$*.hrb
然后,运行一下:
可以看出,大小确实小了很多!! oh,对了我忘记说字符库的压缩了:
我们将需要载入的字符库进行压缩一下,主要使用如下命令:
bim2bin -osacmp in:hzk16s.org out:hzk16s.fnt
这里,我们先将原本的hzk16s 重命名为了hzk16s.org 然后再使用bim2bin.exe文件进行了压缩:
而且系统没啥问题:
不过呢,这里不知道是qemu的问题还是压缩器的问题,如果压缩了中文字符库可能会出bug,因此啊大家自行尝试了。
软件
软件部分,我就不详述了,毕竟我们主要是为了了解操作系统的。这里直接看书以及参考本文的源代码即可。不过对于文本阅读器这里,我们要增加一段:
if (lang == 3 ) { /* EUC */
if (*p == 0x09) {
x = puttab(x, w, xskip, s, tab);
p++;
} else if (0xa1 <= *p && *p <= 0xfe) {
/* ‘SŠp•¶Žš */
if (x == -1) {
s[0] = ' ';
}
if (0 <= x && x < w - 1) {
s[x] = *p;
s[x + 1] = p[1];
}
if (x == w - 1) {
s[x] = ' ';
}
x += 2;
p += 2;
} else {
if (0 <= x && x < w) {
s[x] = *p;
}
x++;
p++;
}
}
这样子就可以支持中文的文字显示了!!!
二、ipl的改良
由于这里我的中文字符库并未压缩,因此这里我们虽然使用了快速读入,但是我读取的柱区依旧很多:
; haribote-ipl
; TAB=4
CYLS EQU 30 ; 声明CYLS=10
ORG 0x7c00 ; 指明程序装载地址
; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy code
JMP entry
DB 0x90
DB "HARIBOTE" ; 启动扇区名称(8字节)
DW 512 ; 每个扇区(sector)大小(必须512字节)
DB 1 ; 簇(cluster)大小(必须为1个扇区)
DW 1 ; FAT起始位置(一般为第一个扇区)
DB 2 ; FAT个数(必须为2)
DW 224 ; 根目录大小(一般为224项)
DW 2880 ; 该磁盘大小(必须为2880扇区1440*1024/512)
DB 0xf0 ; 磁盘类型(必须为0xf0)
DW 9 ; FAT的长度(必9扇区)
DW 18 ; 一个磁道(track)有几个扇区(必须为18)
DW 2 ; 磁头数(必2)
DD 0 ; 不使用分区,必须是0
DD 2880 ; 重写一次磁盘大小
DB 0,0,0x29 ; 意义不明(固定)
DD 0xffffffff ; (可能是)卷标号码
DB "HARIBOTEOS " ; 磁盘的名称(必须为11字?,不足填空格)
DB "FAT12 " ; 磁盘格式名称(必??8字?,不足填空格)
RESB 18 ; 先空出18字节
; 程序主体
entry:
MOV AX,0 ; 初始化寄存器
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
; 读取磁盘
MOV AX,0x0820
MOV ES,AX
MOV CH,0 ; 柱面0
MOV DH,0 ; 磁头0
MOV CL,2 ; 扇区2
MOV BX,18*2*CYLS-1 ; 要读取的合计扇区数
CALL readfast ; 告诉读取
; 读取完毕,跳转到haribote.sys执行!
MOV [0x0ff0],CH ; 记录IPL实际读取了多少内容
JMP 0xc200
error:
MOV AX,0
MOV ES,AX
MOV SI,msg
putloop:
MOV AL,[SI]
ADD SI,1 ; 给SI加1
CMP AL,0
JE fin
MOV AH,0x0e ; 显示一个文字
MOV BX,15 ; 指定字符颜色
INT 0x10 ; 调用显卡BIOS
JMP putloop
fin:
HLT ; 让CPU停止,等待指令
JMP fin ; 无限循环
msg:
DB 0x0a, 0x0a ; 换行两次
DB "load error"
DB 0x0a ; 换行
DB 0
readfast: ; 使用AL尽量一次性读取数据 从此开始
; ES:读取地址, CH:柱面, DH:磁头, CL:扇区, BX:读取扇区数
MOV AX,ES ; < 通过ES计算AL的最大值 >
SHL AX,3 ; 将AX除以32,将结果存入AH(SHL是左移位指令)
AND AH,0x7f ; AH是AH除以128所得的余数(512*128=64K)
MOV AL,128 ; AL = 128 - AH; AH是AH除以128所得的余数(512*128=64K)
SUB AL,AH
MOV AH,BL ; < 通过BX计算AL的最大值并存入AH >
CMP BH,0 ; if (BH != 0) { AH = 18; }
JE .skip1
MOV AH,18
.skip1:
CMP AL,AH ; if (AL > AH) { AL = AH; }
JBE .skip2
MOV AL,AH
.skip2:
MOV AH,19 ; < 通过CL计算AL的最大值并存入AH >
SUB AH,CL ; AH = 19 - CL;
CMP AL,AH ; if (AL > AH) { AL = AH; }
JBE .skip3
MOV AL,AH
.skip3:
PUSH BX
MOV SI,0 ; 计算失败次数的寄存器
retry:
MOV AH,0x02 ; AH=0x02 : 读取磁盘
MOV BX,0
MOV DL,0x00 ; A盘
PUSH ES
PUSH DX
PUSH CX
PUSH AX
INT 0x13 ; 调用磁盘BIOS
JNC next ; 没有出错的话则跳转至next
ADD SI,1 ; 将SI加1
CMP SI,5 ; 将SI与5比较
JAE error ; SI >= 5则跳转至error
MOV AH,0x00
MOV DL,0x00 ; A盘
INT 0x13 ; 驱动器重置
POP AX
POP CX
POP DX
POP ES
JMP retry
next:
POP AX
POP CX
POP DX
POP BX ; 将ES的内容存入BX
SHR BX,5 ; 将BX由16字节为单位转换为512字节为单位
MOV AH,0
ADD BX,AX ; BX += AL;
SHL BX,5 ; 将BX由512字节为单位转换为16字节为单位
MOV ES,BX ; 相当于EX += AL * 0x20;
POP BX
SUB BX,AX
JZ .ret
ADD CL,AL ; 将CL加上AL
CMP CL,18 ; 将CL与18比较
JBE readfast ; CL <= 18则跳转至readfast
MOV CL,1
ADD DH,1
CMP DH,2
JB readfast ; DH < 2则跳转至readfast
MOV DH,0
ADD CH,1
JMP readfast
.ret:
RET
RESB 0x7dfe-$ ; 到0x7dfe为止用0x00填充的指令
DB 0x55, 0xaa
使用了快速读入!!
三、制作ISO映像
点击打开fdtoiso.exe这个软件,然后按下图步骤进行即可:
四、
下载地址:https://sourceforge.net/projects/cdrtfe/
点击这个软件:
选择中文模式:
点击文件系统
点击【制作引导光盘】,然后点击下方的【浏览】,选择对应的软盘 img文件。
然后还需要点击【光盘选项】后,设置:
点击确定后,我们就可以把想载入的一些其他文件拖到软件的窗口里,并点击【开始】的按钮就可以进行刻录了。
然后就会看到下面有生成的信息了!!
总结
Yuan - OS
历经接近一个月的磨练,最终实现了一个操作系统的雏形。
我制作的YuanOS一共 39.8 KB,主要实现了以下的功能:
1、开启了32位的保护模式;
2、能够实现1024 x 768 x 8的画面模式;
3、实现了GDT(全局段号记录表)、IDT(内存中断管理表)与LDT(程序段管理表);
4、实现了内存的动态管理与分配;
5、实现了利用图层技术显示窗口与画面;
6、实现了定时器的功能;
7、实现了内存保护机制(并利用程序进行了越权访问的测试);
8、实现了多任务;
9、实现了API的制作以及应用程序的制作;
10、实现了命令行窗口;
11、实现了中文和日文的显示,并可以无缝切换;
12、能够看图片,听音乐、打游戏等等。
虽然有可能会有大佬不屑一顾这个雏形,但对于我这个初学者来说,我已经从中学到许多了。收获颇丰,虽然中间过程蛮辛苦的,也曾想过放弃,但是咬咬牙努力的坚持了下来,最终有这个收获我还是非常开心的!!!
最后来张全家福,嘿嘿嘿!