写在前面:这是我在学习了一个学期的《30天自制操作系统》之后,用了差不多一个周的时间,所写出来的一个纸娃娃操作系统,有一个小的开机动画,可以输入密码,然后有一个小的画图程序。现在把他写出来,只是为了记录这一个学期的学习经历吧,至少证明自己学有所成,所有所获。
一、实验环境
硬件环境:计算机,无特殊配置要求
软件环境:QEMU虚拟机、批处理文件、相关编译工具等
语言环境:C语言、汇编语言
二、功能介绍及运行效果截图
1、开机:
a、通过使用定时器timer3实现对时间的严格把控
每0.1秒向缓冲区发送一个“1”
b、通过定义图层,实现开机动画
每一个图层都是开机动画的一帧,都是由一个85x80的数组实现的,写在我新建立的start.c文件中,通过不同的符号表示不同的颜色,来实现一张图案的绘制,多个图案连在一起,就构成了一段开机动画,并且可以色彩变换:
c、首先定义图层的级别,最先要展示的图层设置为0级,其余的图层设置为-1级进行隐藏。同时,将每个图层的位置设置为(0,0)左上角。
然后,通过一个while死循环,跳出循环的条件是我所设置的一个标志量flagtime为10,而flagtime会在每次timer3向缓冲区输入一个1的时候进行增加,并且每0.6秒增加一次,然后根据不同的flagtime的值,来将不同的图层的级别设置为1,表示进行展示,而将当前的图层设置为-1,即进行隐藏
就可以设置一个长达6秒的开机动画。
d、在开机动画结束后,注意要将当前图层设置为隐藏的-1级别,而将我们下面要用的密码图层设置为1展示级别
由于为了让我的动画更炫酷一点,我增加了一个圆形的加载形式
并且,因为如果将所有的代码放到bootpack.c文件中,代码会运行的很慢,所以我在start.c文件中实现了对所有的开机动画图层的初始化:
而增加的一个圆形的加载,是在这里实现的
可以实现一个类似于圆形加载的效果,只不过更炫酷一点
效果如下:
然后,为了能够比较平滑的连接到密码输入界面,我需要考虑如何将这个开机动画回归到黑色的界面。
首先,加一个剑的移动的函数分支:
增加了这个函数分支之后,随着我给的时间表胡子良flagtime(在函数中为形参t)随时间增大,将会进入50的分支,进入该分支后,将会对剑图案的位置进行移动。
同时,需要对圆环的位置进行移动,增加一个分支:
通过这个分支,可以实现圆环的移动。
仅仅移动是不够的,我还要实现退出,在这里做一个雪花飘落的样子,就是增加如下的函数分支:
通过取余操作,可以实现类似于雪花的效果,最终结果如下所示:
2、输入密码:
a、首先通过字符数组的形式定义一个正确的账号和密码:
b、再定义另外两个字符数组用来存储我们所输入的字符
c、在一个while死循环内,跳出循环的条件是标志量flagkaiji为1,即输入正确的账号和密码并且同时按下了回车键。
首先会对缓冲区进行判断,如果缓冲区中有值并且这个值在256到511之间,表示此时是键盘输出产生的中断,然后我们对中断进行判断,用sprintf函数将整型数据转换为两位十六进制类型,然后用我写的panduan函数进行判断是什么按键被按下:
如果按下的按键并不是break或者是shift这种功能键,就进行判断。
如果是回车键,需要通过strcmp函数判断当前输入的字符串与正确的字符串,如果相等就对标志量flagkaiji进行赋值为1,并将当前的密码图层设置为隐藏的-1级别。
如果不相等,需要判断有没有输完,如果只是输完了账号一栏,说明我们还需要输入密码一栏,将我们的输入位置j和h调整为密码栏的位置即可:
如果已经输入完全了,说明此时输入者并不知道密码是什么,或者说输入的密码和账号是错误的,这里就可以将输入栏和密码栏进行刷新,然后将输入为止重新进行调整为开头,并输出一行提示错误的字符串“passwords error!!!”,注意,还需要将我们的存储数组进行清空。
最终结果如下:
输入密码界面:
如果输入错误的界面,会出现叉号,并可以重新输入:
重新输入正确的密码则会开机,同样的,在这里设计了一个比较厉害的动画:
4、程序:
这里我的程序是直接使用的节点考试时所编写的画图程序,实现功能很多,这里会直接根据我的主函数,花费大量篇幅进行讲解,每一个功能会进行标红加粗处理:
if (flagshuangji == 0) {
shuangji++;}
if (shuangji == 10000) {
flagshuangji = 1;
shuangji = 0;}
sprintf(s, "%010d", shuangji);
sheet_refresh(sht_back, 100, 80, 320, 100);
这是我用来判断双击和函数,通过标志量flagshaungji的值来表示两次鼠标左键点击之间的时间间隔,如果时间间隔足够小,说明是双击。
io_cli();//关闭终端
if (fifo32_status(&fifo) == 0) {
io_sti();//如果缓冲区为空则开启中断
} else {
//如果缓冲区不为空,说明有服务请求运行,或者有中断发生
i = fifo32_get(&fifo);//取出缓冲区的值,进行判断
io_sti();
if (256 <= i && i <= 511) {
//如果是键盘操作
sprintf(s, "%02X", i - 256);
putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, s, 2);
panduan(s);
功能1、键盘输入:可以通过键盘输入字符,通过我自己编写的panduan函数判断所按下的按键是哪一个按键,并作出相应的操作
if (strcmp(sss, "break") != 0 && strcmp(sss, "left shift") != 0 && strcmp(sss, "return") != 0 && flagwenben == 2 && flagguanbi == 0) {
if (strcmp(sss, "hc") == 0) {
boxfill8(buf_bigwin, binfo->scrnx, dise, j + 3, 16 * (h), j + 3, 16 * (h + 1) - 1);
jj[h] = j;
h = h + 1;
if (h >= binfo->scrny/16-1) {
h = binfo->scrny/16-1;}
j = wbj;
boxfill8(buf_bigwin, binfo->scrnx, zise, j + 3, 16 * (h), j + 3, 16 * (h + 1) - 1);}
/*如果按下的是回车键,那么需要实现换行操作,同时记录当前光标的位置,为下面的删除键操作做铺垫*/
else if (strcmp(sss, "tg") == 0) {
boxfill8(buf_bigwin, binfo->scrnx, dise, j + 3, 16 * (h), j + 3, 16 * (h + 1) - 1);
j = j - 8*zihao;
if (j < wbj) {
h = h - 1;
if (h < 2) {
h = 2;
j = wbj;
}else {
if (jj[h] <= wbj) {
j = wbj;
}else {
if (jj[h] <= wbj) {
j = wbj;
}else {
j = jj[h];
}}}}
boxfill8(buf_bigwin, binfo->scrnx, dise, j + 3, 16 * h, j + 3 + 8*zihao, 16 * h + 16);
boxfill8(buf_bigwin, binfo->scrnx, zise, j + 3, 16 * (h), j + 3, 16 * (h + 1) - 1);}
/*如果按下的是删除键,那么就对前一个字符的位置(8*16像素)进行背景色的填充处理,但需要判断的是是否需要返回上一行,如果是则取出之前在回车处理中所存储的jj数组的相应位置的值*/
else if (strcmp(sss, "dx") == 0) {
flagdx = !flagdx;}
/*如果按下的是大小写切换键,那么就需要将标志量设置为相反的,以便为下面的输出做准备*/
else {
boxfill8(buf_bigwin, binfo->scrnx, dise, j + 3, 16 * h, j + 3 + 8*zihao, 16 * h + 16);
if (flagdx == 0) {
dahuaxiao(sss);}
putfonts8_num(buf_bigwin, binfo->scrnx, j + 3, 16 * h, zise, sss,zihao);
j = j + 8*zihao;
if (j >= binfo->scrnx-5) {
jj[h] = j - 8*zihao;
h = h + 1;
if (jj[h] <= wbj) {
j = wbj;
}else {
j = jj[h];}}
boxfill8(buf_bigwin, binfo->scrnx, zise, j + 3, 16 * h, j + 3, 16 * h + 16 - 1);
}}
sheet_refresh(sht_bigwin, 0, 0, binfo->scrnx, binfo->scrny-20);}
/*如果是正常的按键输入,那么就通过我所写的函数进行字符的打印输出,这里就是我实现的第二个功能:*/
功能2、改变字号:通过我专门编写的putfonts8_num函数,可以输出不同字号的字符,打印在相应的位置,要注意的是需要判断是否需要换行
else if (512 <= i && i <= 767) {
//如果是鼠标操作
if (mouse_decode(&mdec, i - 512) != 0) {
sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);//打印鼠标的位置
if ((mdec.btn & 0x01) != 0) {
s[1] = 'L';}//判断按键状态
if ((mdec.btn & 0x02) != 0) {
s[3] = 'R';}
if ((mdec.btn & 0x04) != 0) {
s[2] = 'C';}
putfonts8_asc_sht(sht_back, 32, 16, COL8_FFFFFF, COL8_008484, s, 15);
功能3、正常窗口拖动:可以拖动正常大小(180x140像素)的窗口,并且和鼠标一样,左边和上边不能出界,下边和右边可以出界
if ((wx + 0 <= mx && mx <= wx + 120 && wy + 3 <= my && my <= wy + 20) && flag2 == 1 && flag == 1 && (mdec.btn & 0x01) != 0 && flagguanbi == 0) {
//如果鼠标在窗口任务栏位置发生了点击并移动的操作
mx += mdec.x;
my += mdec.y;
wx += mdec.x;
wy += mdec.y;}
功能4、正常窗口画细线:可以在正常大小的窗口中实现画细线的操作,并且限定了画线的范围,随着鼠标的移动和点击,实现画线操作
else if ((wx + 5 <= mx && mx <= wx + 175 && wy + 25 <= my && my <= wy + 135) && flag2 == 1 && flag == 1 && flagxiangpi == 0 && flagcu == 0 && flagguanbi == 0 && flagquse == 0 && flagtianchong == 0) {
//如果鼠标在窗口的范围内并且窗口处于正常的打开状态
fx = mx;
fy = my;
mx += mdec.x;
my += mdec.y;
int ax = mx, ay = my;
if ((mdec.btn & 0x01) != 0) {
if (wx + 5 <= mx && mx <= wx + 175 && wy + 25 <= my && my <= wy + 135) {
if ((fx > mx && my > fy) || (fx < mx && my < fy)) {
xian(buf_window, 180, zise, min(fx, mx) - wx, min(fy, my) - wy, max(fx, mx) - wx, max(fy, my) - wy, 1);
xian(buf_bigwin, binfo->scrnx, zise, min(fx, mx) - wx, min(fy, my) - wy, max(fx, mx) - wx, max(fy, my) - wy, 1);}
else {
xian(buf_window, 180, zise, min(fx, mx) - wx, min(fy, my) - wy, max(fx, mx) - wx, max(fy, my) - wy, 0);
xian(buf_bigwin, binfo->scrnx, zise, min(fx, mx) - wx, min(fy, my) - wy, max(fx, mx) - wx, max(fy