今天的主要任务就是文件的操作和文字的显示,今天为了能够支持中文可能会比较困难一些,大家一起加油加油!!
`
接着,我们来说一说alloca是什么。 在c编译器中有着这样一条规定:即如果栈中变量超过了4KB,则就需要调用_alloca这个函数了。其功能是根据操作系统的规格来获取栈空间。(如果不调用这个,仅对ESP进行减法运算是无法成功获取内存的。当然小于4KB时候是可以的)
因此,我们下面给出三个代码程序来看看先:
sosu.c:求1000以内的质数。这一个显示没问题!栈空间未超过4KB
#include
#include "apilib.h"
#define MAX 1000
void HariMain(void)
{
char flag[MAX], s[8];
int i, j;
for (i = 0; i < MAX; i++) {
flag[i] = 0;
}
for (i = 2; i < MAX; i++) {
if (flag[i] == 0) {
sprintf(s, "%d ", i);
api_putstr0(s);
for (j = i * 2; j < MAX; j += i) {
flag[j] = 1;
}
}
}
api_end();
}
sosu2.c:求10000以内的质数。这一个无法显示!栈空间约为10KB
#include
#include "apilib.h"
#define MAX 10000
void HariMain(void)
{
char flag[MAX], s[8];
int i, j;
for (i = 0; i < MAX; i++) {
flag[i] = 0;
}
for (i = 2; i < MAX; i++) {
if (flag[i] == 0) {
sprintf(s, "%d ", i);
api_putstr0(s);
for (j = i * 2; j < MAX; j += i) {
flag[j] = 1;
}
}
}
api_end();
}
sosu3.c:求10000以内的质数。事先给程序分配10KB的内存空间的话,就可以运行了!
#include
#include "apilib.h"
#define MAX 10000
void HariMain(void)
{
char *flag, s[8];
int i, j;
api_initmalloc();
flag = api_malloc(MAX);
for (i = 0; i < MAX; i++) {
flag[i] = 0;
}
for (i = 2; i < MAX; i++) {
if (flag[i] == 0) {
sprintf(s, "%d ", i);
api_putstr0(s);
for (j = i * 2; j < MAX; j += i) {
flag[j] = 1;
}
}
}
api_end();
}
_alloca函数只在下面情况下会被c语言的程序进行调用(采用近转移)
因此,我们制作如下的程序,并放于qpilib中:
[FORMAT "WCOFF"]
[INSTRSET "i486p"]
[BITS 32]
[FILE "alloca.nas"]
GLOBAL __alloca
[SECTION .text]
__alloca:
ADD EAX,-4
SUB ESP,EAX
JMP DWORD [ESP+EAX] ;代替ret
这里,根据我们上面所说的基础,再调用这个时候,我们会先把EIP压栈,此时需要esp - 4 。 因此,为了最后我们能够还原到调用该程序前的esp的值,再eax这里我们需要减去4 。 比如:
eax = 10 ,esp = 20; 当我们调用函数后: esp = 16 .此时,如果直接用esp - eax的话,那么esp = 6 了,那么我们后面的jmp就会跳转到 esp = 10 +6 = 16上和原来调用前的不一样了!!因此,如果我们先eax-4 ,则esp -= eax后,esp = 10.那么此时jmp, 就会跳转到esp = 20处,完美复原了!!
那么制作完这个后,suso2就可以运行了:
而且,还可以利用这个修改winhelo这几个程序,将他们在函数外部声明的buf放到函数内部,利用合适的栈空间保存变量值,这样子就可以极大的减少程序的大小了!!!
对吧!!小了很多呀,这是因为在每修改前的buf是作为数据段,将7.5kb的buf也存了进去了!!(别忘了还要再makedile中写入栈空间大小喔)
一般来说,输入与输出的文件都会具有以下功能
因此,设计如下的API:
打开文件:
关闭文件:
文件定位:
获取文件大小:
文件读取:
首先,我们要对任务的结构做出一些改变,以适应文件的操作:
struct TASK
{
int sel, flags; //sel用于存放GDT编号
int level, priority; //设置优先级
struct FIFO32 fifo;
struct TSS32 tss;
struct SEGMENT_DESCRIPTOR ldt[2];
struct CONSOLE *cons;
int ds_base, cons_stack;//数据段地址;栈的地址
struct FILEHANDLE *fhandle;
int *fat;
};
struct FILEHANDLE
{
char *buf;
int size;
int pos;
};
然后,添加对应的api:
console.c:
void console_task(struct SHEET *sheet, unsigned int memtotal)
{
略
struct FILEHANDLE fhandle[8];
if (sheet != 0) {
cons.timer = timer_alloc();
timer_init(cons.timer, &task->fifo, 1);
timer_settime(cons.timer, 50);
}
file_readfat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200));
for(i = 0; i < 8; i++){
fhandle[i].buf = 0;//未使用
}
task->fhandle = fhandle;
task->fat = fat;
略
}
int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
{
略
for(i = 0;i < 8; i++){
if(task->fhandle[i].buf != 0){//将为关闭的文件关闭
memman_free_4k(memman, (int) task->fhandle[i].buf, task->fhandle[i].size);
task->fhandle[i].buf = 0;
}
}
timer_cancelall(&task->fifo);
memman_free_4k(memman, (int) q, segsiz);
略
}
int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{
略
struct FILEINFO *finfo;
struct FILRHANDLE *fh;
struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
略
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;
file_loadfile(finfo->clustno, finfo->size, fh->buf,
task->fat, (char *) (ADR_DISKIMG + 0x003e00));
}
}
} else if (edx == 22){//关闭文件
fh = (struct FILEHANDLE *) eax;
memman_free_4k(memman, (int) fh->buf, fh->size);
fh->buf = 0;
} else if (edx == 23){//文件光标
if (ecx == 0) {//先按照定位模式,确定文件从哪开始读起,然后文件光标再从该点处移动EBX
fh->pos = ebx;
} else if (ecx == 1) {
fh->pos += ebx;
} else if (ecx == 2) {
fh->pos = fh->size + ebx;
}
//修正文件光标的位置
if (fh->pos < 0) {
fh->pos = 0;
}
if (fh->pos > fh->size) {
fh->pos = fh->size;
}
}else if (edx == 24) {//获取文件大小
fh = (struct FILEHANDLE *) eax;
if (ecx == 0) {
reg[7] = fh->size;
} else if (ecx == 1) {
reg[7] = fh->pos;
} else if (ecx == 2) {
reg[7] = fh->pos - fh->size;
}
} else if (edx == 25) {//文件读取
fh = (struct FILEHANDLE *) eax;
for (i = 0; i < ecx; i++) {
if (fh->pos == fh->size) {//当文件读取的位置达到最大的文件最大地址时,退出
break;
}
*((char *) ebx + ds_base + i) = fh->buf[fh->pos];
fh->pos++;
}
reg[7] = i;
}
然后,添加api进入即可。 接下来我们试写一个小程序看看:
#include "apilib.h"
void HariMain(void)
{
int fh;
char c;
fh = api_fopen("ipl10.nas");
if (fh != 0) {
for (;;) {
if (api_fread(&c, 1, fh) == 0) {
break;
}
api_putchar(c);
}
}
api_end();
}
然后,运行看看:
以下,我们将利用上面的应用程序来代替type命令。但是,为了能够显示任意的文件,我们需要能够接收从命令行输入到的信息,因此,以下我们将制作一个接收命令行数据的API:
struct TASK
{
int sel, flags; //sel用于存放GDT编号
int level, priority; //设置优先级
struct FIFO32 fifo;
struct TSS32 tss;
struct SEGMENT_DESCRIPTOR ldt[2];
struct CONSOLE *cons;
int ds_base, cons_stack;//数据段的段地址;栈的地址
struct FILEHANDLE *fhandle;//存放应用程序打开的文件的信息
int *fat;
char *cmdline;
};
void console_task(struct SHEET *sheet, unsigned int memtotal)
{
略
task->cons = &cons; //将图层的值,存入bootinfo预留的地址空间当中,以便后续调用时可以从堆栈中传递过来!
task->cmdline = cmdline;
略
}
hrb_api:
else if (ebx == 26){//获取命令行输入的命令
i = 0;
for(;;){
*((char *) ebx + ds_base + i) = task->cmdline[i];
if(task->cmdline[i] == 0){
break;
}
if(i >= ecx){
break;
}
i++;
}
reg[7] = i;
}
最后,来看看type:
#include "apilib.h"
void HariMain(void)
{
int fh;
char c, cmdline[30], *p;
api_cmdline(cmdline, 30);
for (p = cmdline; *p > ' '; p++) { }
for (; *p == ' '; p++) { }
fh = api_fopen(p);
if (fh != 0) {
for (;;) {
if (api_fread(&c, 1, fh) == 0) {
break;
}
api_putchar(c);
}
} else {
api_putstr0("File not found.\n");
}
api_end();
}
这里,我先列出在研究中文显示时候参考到的文章,感谢这些作者大佬给予的帮助!!
桃子-HZK16汉字16*16点阵字库的使用及示例程序
xiaoliangmei-基于HZK16的汉字显示技术
在开始这一部分前呢,建议还是先把书上的日文显示(没时间就简要的看看EUC部分)做一遍,然后再进行实现中文。因为对于中文的实现,和日文起始很类似,甚至说,由于没有了半角字符的干扰(中文都是全角字符的),实现起中文可能更加的简单。
以下,我们先介绍以下gb2312标准下的字符矩阵HZK 16:
HZK16字库里的16×16汉字一共需要256个点来显示,也就是说需要32个字节才能达到显示一个普通汉字的目的。
我们知道一个GB2312汉字是由两个字节编码的,范围为0xA1A1~0xFEFE。A1-A9为符号区,B0-F7为汉字区。每一个区有94个字符(注意:这只是编码的许可范围,不一定都有字型对应,比如符号区就有很多编码空白区域)
这里也就和日文是类似的了,不过中文没有面之说而已。总而言之呢,一个汉字占两个字节,这两个中前一个字节为该汉字的区号,后一个字节为该字的位号。其中,每个区记录94个汉字,位号为该字在该区中的位置。
但是呢,由于我们编程中数组是从0开始算起的,而区号和位号是从1开始的,因此我们再算字符在字符集的偏移位置是还需要多减去1,也就是说最后的寻址位置为:offset = (94(区码-1)+(位码-1))32
当然,为图方便呢我们直接减去0xa1就行了!!
哦!!!!对了这里我之前在尝试过程中遇到过一个大坑!!!
就是说,中文的显示起始和日文有点点不一样。书上讲日文字符的显示是分为左右部分进行拼接起来的两个字节的。但是中文不一样,中文是按上下进行拼接的,下面给出一幅图来提供参考一下:
对,就是这样子,可以很轻易的看出来,前16字节都是在上半部分的!!但是呢,这里为了编程的方便,我们可以设置一个小技巧: 我们发现左边都是偶数字节,右边都是奇数的字节。左右部分呢,都是以2进行递增的,因此呢,我们可以设置好一个字符的地址,分别指向初始的偶地址和初始的奇地址,然后每次递增2就可以实现以左半部分和右半部分的实现了,那么下面给出改造后的代码:
void putfont32(char *vram, int xsize, int x, int y, char c, char *font)
{
int i;
char *p, d /* data */;
for (i = 0; i < 16; i++) { //字符像素点阵的行数循环
p = vram + (y + i) * xsize + x; //字符像素点阵的开始位置
d = font[i * 2];//以传入的初始地址为起始,而后每一轮地址就增加2;详情参看本人博客。。
if ((d & 0x80) != 0) { p[0] = c; } //p代表着字符像素点阵一行的那8位
if ((d & 0x40) != 0) { p[1] = c; }
if ((d & 0x20) != 0) { p[2] = c; }
if ((d & 0x10) != 0) { p[3] = c; }
if ((d & 0x08) != 0) { p[4] = c; }
if ((d & 0x04) != 0) { p[5] = c; }
if ((d & 0x02) != 0) { p[6] = c; }
if ((d & 0x01) != 0) { p[7] = c; }
}
return;
}
这样子,我们在其后的显示部分就可以直接(注意和日文的进行区分喔!!):
putfont32(vram, xsize, x - 8, y, c, font ); /* 左半分 */
putfont32(vram, xsize, x , y, c, font + 1 ); /* 右半分 */
好了,有关中文部分介绍的也差不多了,下面让我们来完成实现中文的所需要修改的代码吧。
struct TASK
{
int sel, flags; //sel用于存放GDT编号
int level, priority; //设置优先级
struct FIFO32 fifo;
struct TSS32 tss;
struct SEGMENT_DESCRIPTOR ldt[2];
struct CONSOLE *cons;
int ds_base, cons_stack;//数据段的段地址;栈的地址
struct FILEHANDLE *fhandle;//存放应用程序打开的文件的信息
int *fat;
char *cmdline;
unsigned char langmode, langbyte1;//mode 表示字符集的种类; bytel用于存储全角字符时的第一个变量,实际上就是用于判断是否是全角字符的
};
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,中文字符集
hzk = (unsigned char *) memman_alloc_4k(memman, 0x5d5d * 32);
fat = (int *) memman_alloc_4k(memman, 4 * 2880);
file_readfat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200));
finfo = file_search("hzk16s", (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
if (finfo != 0) {
file_loadfile(finfo->clustno, finfo->size, hzk, fat, (char *) (ADR_DISKIMG + 0x003e00));
} else {
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)
{
nihongo = (unsigned char *) memman_alloc_4k(memman, 16 * 256 + 32 * 94 * 47);
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) {
file_loadfile(finfo->clustno, finfo->size, nihongo, fat, (char *) (ADR_DISKIMG + 0x003e00));
} else {
for (i = 0; i < 16 * 256; i++) {
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);
}
}
这里的mode 代表着模式 2 ==> 日文 ; 3 ==> 中文。
void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s) //s指向字符串开头的位置,因此可以直接使用s进行读取字符串的每一个单独字符; 以ascii编码
{
extern char hankaku[4096];
struct TASK *task = task_now();
char *nihongo = (char *) *((int *) 0x0fe8), *font;
char *hzk = (char *) *((int *) 0x0fe8);
int k, t;
if (task->langmode == 0) {//内带的字符集
for (; *s != 0x00; s++) {
putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
x += 8;
}
}
if (task->langmode == 1) {//shift jis 下的日文显示
for (; *s != 0x00; s++) {
if (task->langbyte1 == 0) {
if ((0x81 <= *s && *s <= 0x9f) || (0xe0 <= *s && *s <= 0xfc)) {
task->langbyte1 = *s;
} else {
putfont8(vram, xsize, x, y, c, nihongo + *s * 16);
}
} else {
if (0x81 <= task->langbyte1 && task->langbyte1 <= 0x9f) {
k = (task->langbyte1 - 0x81) * 2;
} else {
k = (task->langbyte1 - 0xe0) * 2 + 62;
}
if (0x40 <= *s && *s <= 0x7e) {
t = *s - 0x40;
} else if (0x80 <= *s && *s <= 0x9e) {
t = *s - 0x80 + 63;
} else {
t = *s - 0x9f;
k++;
}
task->langbyte1 = 0;
font = nihongo + 256 * 16 + (k * 94 + t) * 32;
putfont8(vram, xsize, x - 8, y, c, font ); /* 左半分 */
putfont8(vram, xsize, x , y, c, font + 16); /* 右半分 */
}
x += 8;
}
}
if (task->langmode == 2) {//EUC 模式下的日文
for (; *s != 0x00; s++) {
if (task->langbyte1 == 0) {
if (0x81 <= *s && *s <= 0xfe) {
task->langbyte1 = *s;
} else {
putfont8(vram, xsize, x, y, c, nihongo + *s * 16);
}
} else {
k = task->langbyte1 - 0xa1;
t = *s - 0xa1;
task->langbyte1 = 0;
font = nihongo + 256 * 16 + (k * 94 + t) * 32;
putfont8(vram, xsize, x - 8, y, c, font ); /* 左半分 */
putfont8(vram, xsize, x , y, c, font + 16); /* 右半分 */
}
x += 8;
}
}
if (task->langmode == 3) {//GB2312 HKZ16 中文模式
for (; *s != 0x00; s++) {
if (task->langbyte1 == 0) {
if (0xa1 <= *s && *s <= 0xfe) {
task->langbyte1 = *s;
} else {
putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
}
} else {
k = task->langbyte1 - 0xa1;
t = *s - 0xa1;
task->langbyte1 = 0;
font = hzk + (k * 94 + t) * 32;
putfont32(vram, xsize, x - 8, y, c, font ); /* 左半分 */
putfont32(vram, xsize, x , y, c, font + 1 ); /* 右半分 */
}
x += 8;
}
}
return;
}
void console_task(struct SHEET *sheet, unsigned int memtotal)
{
略
unsigned char *nihongo = (char *) *((int *) 0x0fe8);
略
if (nihongo[0] != 0xff) { /* 是否载入了中文字符库 */
task->langmode = 3;
} else {
task->langmode = 0;
}
task->langbyte1= 0;
}
int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
{
略
timer_cancelall(&task->fifo);
memman_free_4k(memman, (int) q, segsiz);
task->langbyte1 = 0;
略
}
void cons_runcmd(char *cmdline, struct CONSOLE *cons, int *fat, unsigned int memtotal)
{
略
else if (strncmp(cmdline, "langmode ", 9) == 0) {
cmd_langmode(cons, cmdline);
}
略
}
void cmd_langmode(struct CONSOLE *cons, char *cmdline)
{//进行设置模式,0是英文模式,1是中文模式
struct TASK *task = task_now();
unsigned char mode = cmdline[9] - '0';
if (mode <= 3) {
task->langmode = mode;
font_init(task->langmode);
} else {
cons_putstr0(cons, "mode number error.\n");
}
cons_newline(cons);
return;
}
void cons_newline(struct CONSOLE *cons)
{
略
if(task->langmode == 3 && task->langbyte1 != 0){
cons->cur_x += 8;
}
return;
}
int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{
略
else if (edx == 27){
reg[7] = task->langmode;
}
当然,由于我们这里有了全角字符,但是我们之前刷新却只刷半角字符,因此如果不修改的话可能会刷新出错:
void putfonts8_asc_sht(struct SHEET *sht, int x, int y, int c, int b, char *s, int l)
{
struct TASK *task = task_now();
boxfill8(sht->buf, sht->bxsize, b, x, y, x + l * 8 - 1, y + 15);
if(task->langmode != 0 && task->langbyte1 != 0){
putfonts8_asc(sht->buf, sht->bxsize, x, y, c, s);
sheet_refresh(sht, x - 8, y, x + l * 8, y + 16);
}else{
putfonts8_asc(sht->buf, sht->bxsize, x, y, c, s);
sheet_refresh(sht, x, y, x + l * 8, y + 16);
}
return;
}
最后,修改一下主函数:
略
task_a->langmode = 3;
略
font_init(task_a->langmode);
略
哦,差点忘了,由于我们的字符库是262KB左右大大小,因此我们需要更改一下ipl10.我们讲cyls设置为30,以便读入更多的柱面,30个柱面的话大概有:
30 * 18 * 2 * 512 = 552960 字节 = 540 kb 。 已经足够大了
然后,在把makefile里的ipl10的名字改一下就好了!! 当然你想读入20也行。这里我读入30是因为,作者本人名字问题,如果读入20啥都显示不出来,(明明是很常见的字,几大姓氏来着,都不能显示,我也是很无语了)
这里,我们准备很多中文字符库:
然后,我们导入其中一个看看:
然后,写入一个程序:
chklang:
#include "apilib.h"
void HariMain(void)
{
int langmode = api_getlang();
static char s1[23] = {
0x93, 0xfa, 0x96, 0x7b, 0x8c, 0xea, 0x83, 0x56, 0x83, 0x74, 0x83, 0x67,
0x4a, 0x49, 0x53, 0x83, 0x82, 0x81, 0x5b, 0x83, 0x68, 0x0a, 0x00
};
static char s2[17] = {
0xc6, 0xfc, 0xcb, 0xdc, 0xb8, 0xec, 0x45, 0x55, 0x43, 0xa5, 0xe2, 0xa1,
0xbc, 0xa5, 0xc9, 0x0a, 0x00
};
if (langmode == 0) {
api_putstr0("English ASCII mode\n");
}
if (langmode == 1) {
api_putstr0(s1);
}
if (langmode == 2) {
api_putstr0(s2);
}
if (langmode == 3) {
api_putstr0("Yuan-OS 欢迎来到源的系统");
}
api_end();
}
然后我还写了一个文本文件:
ecu.txt
注意:我们写入的代码或者文本都要以GB2312编码进行保存!!
然后,让我们来看看:
哦耶!! 那我们在换一个字体看看:
如果上面搞完了没啥问题的话,仅需改变这两个地方就行了:
看出区别了吗?好像是变了,嘿嘿嘿,那我们再看多几个:
嘿嘿嘿,这个区别大了!! 成功成功了,撒花★,°:.☆( ̄▽ ̄)/$:.°★ 。
哦!! 我还忘了一件事,就是试试看能不能仅需日文的转化,毕竟我们都写了转化的函数了:如图:
哇哦成功了,呼~说实话,这里我已经花了可不止一天时间了。不过最终还是成功了,感觉还是非常非常之开心的,超有成就感是不是,嘿嘿嘿!
呼,当我写到这的时候,起码应该有一天半以上的时间了。花费的时间还是很多的。虽然在查资料啊,制作的过程中我感觉非常困难,可是当我写到这时,回过头一看,貌似这个显示中文好像也没那么难,对吧!!不过总而言之呢,能显示出中文我还是非常兴奋的😊🤞
那么,兄弟姐妹们,明天见!!