• 自制操作系统日志——第二十八天(实现中文字符显示)


    自制操作系统日志——第二十八天

    今天的主要任务就是文件的操作和文字的显示,今天为了能够支持中文可能会比较困难一些,大家一起加油加油!!



    `

    一、alloca

    预备知识(有关c函数的参数、变量在内存里的地址)

    1. 在c中,函数的全局变量(也就是在函数的外部声明或者加上static的变量)都会被解释成DB和RESB,其是不会占用栈空间的,也就是说全局变量位于非代码段,非栈段,而位于data段(初始化)或者bss段(未初始化);
    2. 对于局部变量而言(也就是函数内部声明且不带static的变量)则一般来说就是位于栈段的,通过在在栈中开辟空间来进行保存相应的值,未初始化的局部变量,编译器并不会给它赋初值,而是找到对应的占地址空间拿来就用;
    3. 函数的参数是通过栈传递,而且是从右到左依次入栈;即使是char型变量,在传递参数时,也是占用两个字节,因为push操作默认是两个字节为单位的;
    4. 函数的返回值,通过寄存器进行返回的:char型 AL,int型 AX  ;
    5. 函数调用过程中,除了参数会入栈,还会保存EIP的值,以便后续能够进行ret,例如,sum(a,b) :则下面从高地址到地址的顺序是:
      B
      A
      EIP
      是按这样子进行入栈的,所以在指定栈空间数据的时候千万不要指错了!!

    接着,我们来说一说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();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    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();
    }
    
    • 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

    _alloca函数只在下面情况下会被c语言的程序进行调用(采用近转移)

    • 要执行的操作需要从栈中分配EAX个字节的内存空间; (ESP - = EAX)
    • 遵守如下规则,不能改变ECX、EDX、EBX、EBP、ESI、EDI的值(可以临时改变,但需要还原)

    因此,我们制作如下的程序,并放于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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这里,根据我们上面所说的基础,再调用这个时候,我们会先把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

    一般来说,输入与输出的文件都会具有以下功能

    • 打开 ==> 指定相应的文件名,如果打开成功,则返回对应的文件句柄
    • 关闭 ==> 通过提供的文件句柄进行关闭操作
    • 定位 ==> 指定下次读取,写入命令需要的操作目标位于文件中的哪个位置
    • 读取 ==> 按指定的需要读取的数据长度及内存地址,文件内容会被传送至对应的内存地址
    • 写入 ==> 按指定的需要写入数据的长度及内存地址,文件内容有内存地址送入文件中

    因此,设计如下的API:
    打开文件:

    • EDX = 21
    • EBX = 文件名
    • EAX = 文件句柄(返回0则表示打开失败)

    关闭文件:

    • EDX = 22
    • EAX = 文件句柄

    文件定位:

    • EDX = 23
    • EAX = 文件句柄
    • ECX = 定位模式(0:定位的起点为文件开头;1:定位的起点为当前访问模式;2:定位的起点为文件的末尾)
    • EBX = 定位的偏移量

    获取文件大小:

    • EDX = 24
    • EAX = 文件句柄
    • ECX = 文件大小的获取模式(0:普通文件大小;1:当前读取位置从文件开头起算的偏移量;2:当前读取位置从文件末尾起算的偏移量)
    • EAX = 文件大小

    文件读取:

    • EDX = 25
    • EAX = 文件句柄
    • EBX = 缓冲区地址
    • ECX = 最大读取字节
    • EAX = 本次读取到的字节数

    首先,我们要对任务的结构做出一些改变,以适应文件的操作:

    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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    然后,添加对应的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;}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    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);}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    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;
    	}
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    然后,添加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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    然后,运行看看:
    在这里插入图片描述

    命令行API

    以下,我们将利用上面的应用程序来代替type命令。但是,为了能够显示任意的文件,我们需要能够接收从命令行输入到的信息,因此,以下我们将制作一个接收命令行数据的API:

    • EDX = 26
    • EBX = 存放命令行内容的地址
    • ECX = 最多可存放多少字节
    • EAX = 实际存放了多少字节
    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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    void console_task(struct SHEET *sheet, unsigned int memtotal)
    {
    略
        task->cons = &cons; //将图层的值,存入bootinfo预留的地址空间当中,以便后续调用时可以从堆栈中传递过来!
    	task->cmdline = cmdline;}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    最后,来看看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();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    三、实现中文显示

    这里,我先列出在研究中文显示时候参考到的文章,感谢这些作者大佬给予的帮助!!
    桃子-HZK16汉字16*16点阵字库的使用及示例程序
    xiaoliangmei-基于HZK16的汉字显示技术

    在开始这一部分前呢,建议还是先把书上的日文显示(没时间就简要的看看EUC部分)做一遍,然后再进行实现中文。因为对于中文的实现,和日文起始很类似,甚至说,由于没有了半角字符的干扰(中文都是全角字符的),实现起中文可能更加的简单。

    以下,我们先介绍以下gb2312标准下的字符矩阵HZK 16:

    1. HZK16字库是符合GB2312国家标准的16×16点阵字库,HZK16的GB2312-80支持的汉字有6763个,符号682个。其中一级汉字有 3755个,按声序排列,二级汉字有3008个,按偏旁部首排列。

    HZK16字库里的16×16汉字一共需要256个点来显示,也就是说需要32个字节才能达到显示一个普通汉字的目的

    我们知道一个GB2312汉字是由两个字节编码的,范围为0xA1A1~0xFEFE。A1-A9为符号区,B0-F7为汉字区。每一个区有94个字符(注意:这只是编码的许可范围,不一定都有字型对应,比如符号区就有很多编码空白区域)

    这里也就和日文是类似的了,不过中文没有面之说而已。总而言之呢,一个汉字占两个字节,这两个中前一个字节为该汉字的区号,后一个字节为该字的位号。其中,每个区记录94个汉字,位号为该字在该区中的位置。

    • 区码:汉字的第一个字节-0xA0,因为汉字编码是从0xA0区开始的,所以文件最前面就是从0xA0区开始,要算出相对区码
    • 位码:汉字的第二个字节-0xA0

    但是呢,由于我们编程中数组是从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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这样子,我们在其后的显示部分就可以直接(注意和日文的进行区分喔!!):

    				putfont32(vram, xsize, x - 8, y, c, font     );	/* 左半分 */
    				putfont32(vram, xsize, x    , y, c, font + 1 );	/* 右半分 */
    
    • 1
    • 2

    好了,有关中文部分介绍的也差不多了,下面让我们来完成实现中文的所需要修改的代码吧。

    • bootpack.h:添加需要的变量,以表示字符的类型和用于判断是否是半角字符的变量。
    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用于存储全角字符时的第一个变量,实际上就是用于判断是否是全角字符的
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 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,中文字符集
    		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);
    	}
    }
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    这里的mode 代表着模式 2 ==> 日文 ; 3 ==> 中文。

    • graphic.c: 实现输出字符串!!
    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;
    }
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • console.c: 在命令行中可以进行设定
    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;
    }
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    当然,由于我们这里有了全角字符,但是我们之前刷新却只刷半角字符,因此如果不修改的话可能会刷新出错:

    • windows.c:刷新
    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    最后,修改一下主函数:

    
    略
    task_a->langmode = 3;font_init(task_a->langmode);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    哦,差点忘了,由于我们的字符库是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();
    }
    
    
    • 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

    然后我还写了一个文本文件:
    ecu.txt

    注意:我们写入的代码或者文本都要以GB2312编码进行保存!!

    然后,让我们来看看:
    在这里插入图片描述
    在这里插入图片描述

    哦耶!! 那我们在换一个字体看看:
    如果上面搞完了没啥问题的话,仅需改变这两个地方就行了:
    在这里插入图片描述

    在这里插入图片描述

    看出区别了吗?好像是变了,嘿嘿嘿,那我们再看多几个:

    在这里插入图片描述

    嘿嘿嘿,这个区别大了!! 成功成功了,撒花★,°:.☆( ̄▽ ̄)/$:.°★

    哦!! 我还忘了一件事,就是试试看能不能仅需日文的转化,毕竟我们都写了转化的函数了:如图:
    在这里插入图片描述

    哇哦成功了,呼~说实话,这里我已经花了可不止一天时间了。不过最终还是成功了,感觉还是非常非常之开心的,超有成就感是不是,嘿嘿嘿!


    总结

    呼,当我写到这的时候,起码应该有一天半以上的时间了。花费的时间还是很多的。虽然在查资料啊,制作的过程中我感觉非常困难,可是当我写到这时,回过头一看,貌似这个显示中文好像也没那么难,对吧!!不过总而言之呢,能显示出中文我还是非常兴奋的😊🤞

    那么,兄弟姐妹们,明天见!!

  • 相关阅读:
    零基础学Python有什么建议?如何入门编程?
    YOLOv8血细胞检测(15):微小目标检测的上下文增强和特征细化网络ContextAggregation
    基于docker和cri-dockerd部署k8sv1.26.3
    Linux磁盘扩容(加硬盘法)
    在线协作文档哪家强?微软 Loop 、Notion、FlowUs
    Himall商城文件帮助类IOHelper(1)
    如何实现浏览器标签页之间的通信
    结合 LDAP 和 RADIUS 认证的无线网络安全方案
    华测监测预警系统 2.2 存在任意文件读取漏洞
    pool = multiprocessing.Pool()报错:module object has no attribute Pool
  • 原文地址:https://blog.csdn.net/qq_43696276/article/details/126289062