注:本程序为原创,若发现bug,万望指出,若有问题,欢迎交流,转载请指明出处。若能有助于一二访客,幸甚。
本以为要在裸机上显示个汉字是极难的,没想到亲自动手做一下,也不甚难做。“天下事有难易乎?为之,则难者亦易矣,不为,则易者亦难亦” 古人诚不我欺。
参考:
http://blog.sina.com.cn/s/blog_8c7bf19701010rhn.html
《30天自制操作系统》
关于点阵字体ASC16、HZK16详见上述参考的博客,此一篇足矣。
下面是我的测试代码(当时只为测试,随手写的):
/*************************************************************************
> File: test.c
> Describe: 尝试使用点阵字体显示ASCII 码和汉字
> Author: 孤舟钓客
> Mail: [email protected]
> Time: 2013年01月01日 星期二 00时47分17秒
************************************************************************/
#include
unsigned char font_asc[4096];
unsigned char font_hzk[267616];
int load_asc()
{
FILE *fp;
fp = fopen("./resource/font/ASC16", "rb");
if (fp == NULL)
{
printf ("read ASC16 file failed!");
return 0;
}
fread(font_asc, 1, 4096, fp);
fclose(fp);
return 1;
}
int load_hzk()
{
FILE *fp;
fp = fopen("./resource/font/HZK16", "rb");
if (fp == NULL)
{
printf ("read HZK16 file failed!");
return 0;
}
fread(font_hzk, 1, 267616, fp);
fclose(fp);
return 1;
}
int disp_asc(unsigned char c)
{
unsigned char *pc = font_asc + (int)c * 16;
int i, j;
for (i = 0; i < 16; i++)
{
unsigned char test_bit = 128;
for (j = 0; j < 8; j++)
{
if (*pc & test_bit)
printf("*");
else
printf(" ");
test_bit >>= 1;
}
printf("\n");
pc++;
}
return 1;
}
int disp_hzk(unsigned char ch[3])
{
unsigned char qu_no = ch[0] - 0xa0;
unsigned char wei_no = ch[1] - 0xa0;
unsigned long offset = (94*(qu_no-1) + (wei_no-1)) * 32;
unsigned char *pc = font_hzk + offset;
int i, j;
for (i = 0; i < 32; i++)
{
unsigned char test_bit = 128;
for (j = 0; j < 8; j++)
{
if (*pc & test_bit)
printf("@");
else
printf(" ");
test_bit >>= 1;
}
pc++;
if (i & 1)
printf("\n");
}
return 1;
}
int main()
{
if (!load_asc() || !load_hzk())
{
printf("load font error!");
return 0;
}
unsigned char c = 'G';
disp_asc(c);
unsigned char hz[3] = "钓";
disp_hzk(hz);
disp_hzk("客");
return 0;
}
结果:
本blog只为记录开发过程,以供日后参考,所以不会每次贴所有代码,只贴新的内容,前面若有更改,会指出。
在此过程中,修改两个地方:
1)增加kernel扇区后发现系统不能正常运行。后又发现最多只能支持128个扇区。经bochs单步调试若干次,最后发现每读一个扇区后bx增加512,会导致读入128个扇区后溢出,也就是超出了一个段64KB寻址的能力,所以导致后面的内容覆盖前面的内容,以至于系统不能正常运行。改为每次增加es:
read_kernel:
movw $0x1000, %si
movw %si, %es # ES:BX 为缓冲区地址
xorw %bx, %bx
movw $0x01, %di # 相对扇区号
1:
movw %di, %ax # 将相对扇区号传给AX作为参数
call read_a_sect
addw $512>>4, %si
movw %si, %es
incw %di
cmpw $KERNEL_SECT_NUM+1, %di
jne 1b
ret
2)在init.c中简单定一个了一个指向前面的VIDEO_INFO的结构体指针,发现结果不对。调试发现结构体内存对齐问题。于是简单增加了两个显示模式的参数。
video_mode: # 显示模式
.short 0
screen_x: # 水平分辨率
.short 0
screen_y: # 垂直分辨率
.short 0
bits_per_pixel:
.byte 0
memory_model:
.byte 0
video_ram: # video_ram地址
.long 0
当然,也可以修改init.c中结构体的定义中的内存对齐方式。
load.s中最后调用init,进入C语言文件init.c中定义的init()函数。
init.c本不应有下面的代码,但目前只为测试正确性,所以没有考虑代码的组织,后面这部分代码将会重新组织到其他文件中。
/*************************************************************************
> File: init.c
> Author: 孤舟钓客
> Describe: 主要完成初始化工作
0.绘制矩形,测试C直接写显存以及测试保存的VIDEO_INFO正确性
1.测试显示ASCI字符
2.测试显示汉字
3.显示ASCII与汉字混合的字符串
> Mail: [email protected]
> Time: 2013年01月01日 星期二 17时25分24秒
************************************************************************/
#include "include/types.h"
#include "include/kernel.h"
typedef struct struct_video_info {
WORD video_mode;
WORD screen_x;
WORD screen_y;
BYTE bits_per_pixel;
BYTE memory_model;
BYTE* p_vram;
} struct_video_info;
const struct_video_info* p_video_info = (struct_video_info*)(VIDEO_INFO_ADDR);
COLOR current_color = 6;
static void fill_rectangle(WORD left, WORD right, WORD top, WORD bottom)
{
BYTE* p_vram = p_video_info->p_vram + top*p_video_info->screen_x;
int row, col;
for (row = top; row < bottom; row++)
{
for (col = left; col < right; col++)
p_vram[col] = current_color;
p_vram += 800;
}
}
void display_asc(char ch, int x, int y)
{
BYTE* p_asc = (BYTE*)(FONT_ASC_ADDR) + ch * 16;
BYTE* p_vram = p_video_info->p_vram + y*p_video_info->screen_x;
int i, j;
for (i = 0; i < 16; i++)
{
BYTE test_bit = 128;
BYTE* p_cur_vram = p_vram+x;
for (j = 0; j < 8; j++)
{
if (*p_asc & test_bit)
p_cur_vram[j] = 3;
else
p_cur_vram[j] = 0;
test_bit >>= 1;
}
p_asc++;
p_vram += p_video_info->screen_x;
}
}
void display_hzk(char ch[3], int x, int y)
{
BYTE qu_no = (BYTE)ch[0] - 0xa0;
BYTE wei_no = (BYTE)ch[1] - 0xa0;
DWORD offset= (94*(qu_no-1) + (wei_no-1)) * 32;
BYTE* p_hzk = (BYTE*)(FONT_HZK_ADDR) + offset;
BYTE* p_vram = p_video_info->p_vram + y*p_video_info->screen_x;
int i, j, k;
for (i = 0; i < 16; i++)
{
for (j = 0; j < 2; j++)
{
BYTE test_bit = 128;
BYTE* p_cur_vram = p_vram+x+j*8;
for (k = 0; k < 8; k++)
{
if (*p_hzk & test_bit)
p_cur_vram[k] = 3;
else
p_cur_vram[k] = 0;
test_bit >>= 1;
}
p_hzk++;
}
p_vram += p_video_info->screen_x;
}
}
void test_vram(void)
{
int i;
BYTE* p = p_video_info->p_vram;
for (i = 800*15; i < 800*20; i++)
*(p+i) = 2;
}
void show_logo(int x, int y)
{
BYTE* p_vram = p_video_info->p_vram + y*p_video_info->screen_x;
BYTE* p_logo = (BYTE*)(LOGO_ADDR);
int i, j;
for (i = 0; i < LOGO_CY; i++)
{
BYTE* p_cur = p_vram + x;
for (j = 0; j < LOGO_CX; j++)
{
if (p_logo[j] != 0xff)
p_cur[j] = 4;
else
p_cur[j] = 0;
}
p_vram += p_video_info->screen_x;
p_logo += LOGO_CX;
}
}
/* 实现一个简陋的打印字符串程序,暂不能只能换行,过几天重写 */
void display_string(char* str, int x, int y)
{
char* p = str;
WORD cur_x = x, cur_y = y;
char hzk[3];
hzk[2] = '\0';
while (*p != '\0')
{
if ((*p & 0x80) == 0)
{
display_asc(*p, cur_x, cur_y);
cur_x += 8;
p++;
}
else
{
hzk[0] = *p++;
hzk[1] = *p++;
display_hzk(hzk, cur_x, cur_y);
cur_x += 16;
}
}
}
void init(void)
{
test_vram();
show_logo(400-128, 20);
fill_rectangle(256, 512, 150, 200);
display_asc('B', 300 + 8*0, 200);
display_asc('a', 300 + 8*1, 200);
display_asc('b', 300 + 8*2, 200);
display_asc('y', 300 + 8*3, 200);
display_asc(' ', 300 + 8*4, 200);
display_asc('O', 300 + 8*5, 200);
display_asc('S', 300 + 8*6, 200);
display_hzk("孤", 358 + 16*0, 200);
display_hzk("舟", 358 + 16*1, 200);
display_hzk("钓", 358 + 16*2, 200);
display_hzk("客", 358 + 16*3, 200);
display_string("测试display_string 能不能行~", 300, 220);
display_string("天下风云出我辈,", 300, 250+18*0);
display_string("一入江湖岁月催。", 300, 250+18*1);
display_string("皇图霸业谈笑中,", 300, 250+18*2);
display_string("不胜人生一场醉。", 300, 250+18*3);
display_string("只叹尘事如潮,", 300, 250+18*4);
display_string("人如水!何时归!", 300, 250+18*5);
display_string("提剑跨骑挥鬼蜮,", 300, 250+18*6);
display_string("白骨如山鸟惊飞。", 300, 250+18*7);
display_string("尘事如潮人如水,", 300, 250+18*8);
display_string("只叹江湖几人回。", 300, 250+18*9);
display_string("夜雨八方战孤城,", 300, 250+18*10);
display_string("平明剑气看刀声。", 300, 250+18*11);
display_string("侠骨千年寻不见,", 300, 250+18*12);
display_string("碧血红叶醉秋风。", 300, 250+18*13);
}
1)绘制一个简单的界面
2)重新组织这些代码
3)实现一个稍微优雅些的kprintf 函数
4)中断和异常
5)键盘、鼠标、时钟中断
6)任务切换
7)输入(要实现输入中文难道要自己写个输入法??这个毕业后去搜狗干两年该容易了。。。)
8)内存管理
9)系统调用
10)文件系统
11)终端
12)几个简单应用程序(写过好几次的拼图游戏一定得实现一个!)
13)其他待定
^_^,路曼曼其修远兮~慢慢来,Just for fun。在此中收获一份成就感,收获一份喜悦,足矣。