30天自制操作系统第4天(共17页).docx

上传人:飞****2 文档编号:14015885 上传时间:2022-05-02 格式:DOCX 页数:17 大小:461.51KB
返回 下载 相关 举报
30天自制操作系统第4天(共17页).docx_第1页
第1页 / 共17页
30天自制操作系统第4天(共17页).docx_第2页
第2页 / 共17页
点击查看更多>>
资源描述

《30天自制操作系统第4天(共17页).docx》由会员分享,可在线阅读,更多相关《30天自制操作系统第4天(共17页).docx(17页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、精选优质文档-倾情为你奉上操作系统实验日志学号姓名甘昆禄专业年级班级智能1601实验日期2018.10.17实验项目第4天:C语言与画面显示练习一、实验主要内容1、让C语言读写内存上次实验隐约知道画面显示与vram有关,但具体的对应关系未知。这次我们用C语言来改写vram看看。作者首先用了汇编语言辅助函数,用了简单的方法,便于我们理解。Naskfunc.nas中增加了一个函数可供C语言调用的函数_write_mem8,用于实现直接写入指定内存地址的语句。如果C语言中write_mem8(0x1234,0x56);语句, 则动作上相当于汇编的MOV BYTE0x1234,0x56。第一个数字在内

2、存里的存放地址ESP+4 ,下一个数字的存放就依次累加4。这里两个参数,一个地址,一个数据。汇编与C语言结合时能自由使用的寄存区只有EAX,ECX,EDX3个,其他寄存区用于记忆非常重要的值,只能读不能写。这段代码中还增加了INSTRSET指令,是用来告诉nask这个程序是给486使用的,不然会被默认解释成8086机器使用(偶尔使用)的标签(label)或者常数。C语言代码:void io_hlt(void);void write_mem8(int addr, int data);void HariMain(void) int i; for (int i = 0xa0000; i = 0xaf

3、fff; i+) write_mem8(i, 15); /*将数据15写入地址i*/ for(;) io_hlt(); 2、绘制条纹只需要在bootpack.c中修改写入值”15”为”i&0x0f”:write_mem8(i, i & 0x0f);对图形来说,0和1并不是作为数字来使用,重点是0和1 的排列方式。对于0和1的互相变化,有位运算”或”(OR)运算、”与”(AND)运算和”异或”(XOR)运算。这些我们在数字逻辑和离散数学里都学过。这样,写入的值就高四位变为0,其余不变。就这样,每隔16个像素,色号就反复一次。3、直接用C语言写内存用指针写。指针符号是”*”,*p中的p是地址,而*

4、p是p指向地址的内容。 使用*i = i * 0x0f可直接将i*0x0f写入i指向的内存地址中。 *i = i * 0x0f对应汇编的MOV i, ( i * 0x0f),但如果直接这样写就不清楚i到底是BYTE还是WORD还是DWORD。 由于MOV指令的两个对象必须是相同字节长度,即同类型(BYTE/WORD/DWORD),除非另一方是寄存器才可以省略。同理,在使用指针时需要事先声明它的类型,即指针所指向内容的类型。 char i是类似AL的1字节变量,short i是类似于AX的2字节变量,int i是类似于EAX的4字节变量。char p ; / 用于BYTE类地址 * / shor

5、t p; / 用于 WORD 类 地 址* / int p ; / 用于DWORD 类 地 址 * /以上指针中的p都是4字节,因为p是用于记录地址的变量。在汇编语言中,地址也像ECX一样,用4字节的寄存器来指定,所以也是4字节。p = i; /带入地址/ p = i & 0x0f; /这可以替代write_mem8(i, i&0x0f)*/在执行make run之后出现了“warning: assignment makes pointer from integer without a cast”这句话。在C语言中,普通数值和表示内存地址的数值被认为是两种不同的东西。如果将普通整数值赋给内存地

6、址变量就会有警告,可以在赋值的时候使用强制类型转换: p = (char * ) i; /*注意i的类型要和p类型一样*/指针应用p = (char *) 0xa0000; /*给地址变量赋值*/for (int i = 0; i = 0xffff; +i) *(p + i) = i & 0x0f;在声明p的时候给它赋值为写入内存的起始地址,之后i作为地址增量,由p+i来指定写入内存的地址。C语言中,*(p+i)还可以改写成pi这种形式:p = (char *) 0xa0000; /*给地址变量赋值*/for (int i = 0; i = 0xffff; +i) pi = i & 0x0f;

7、pi与*(p + i)意思相同 ,这两者的差距只有前者4个字符,后者6个字符。但是pi并不能说是数组,只是一个看起来像是数列的使用了地址变量的省略写法而已。加法运算可以交换顺序,于是(p+i)和(i+p),pi和ip,a2和2a都是一个意思,这更能说明它们与数组没有关系。4、调色板这次我们使用的是320*200的8位颜色模式,只有256种颜色,而计算机表示表示颜色时,都是用#ffffff一类的数,就是RGB表示法,可以表示256*256*256种颜色。那么我们现在8位数怎么表示颜色呢,其实我们这次只用到了16种。给每种颜色编上号码,像这样就可以使用了。再根据作者讲解修改完bootpack.c中

8、的代码后,作者以汇编的角度解说table_rgb的声明部分。RESB指令是“reserve byte”的略写预约字节,如果想要从当前位置向后空出3个字节来,并且填0,就可以用RESB 3在RESB 3前面加上地址就变成了:a: RESB 3与C语言中的char a3一个意思。但是汇编中RESB的内容能够保证是0,但是C语言不能保证,因此需要在这个声明后加上“=”,还可以写上数据的初始值。 如char a3 = 1, 2, 3; 即Char a3;a0 = 1;a1 = 2;a2 = 3;a是表示最初地址的数字,也就是说它被认为是指针。像上面两种声明方式,汇编时都是要用到赋值语句的,作者说这样很

9、浪费字节。而在声明前加上static就可以将汇编的RESB指令代替成DB指令,这样它在内存中的存储位置就变了,并且未初始化的全局静态变量会被程序自动初始化为0。而且在程序运行之前,static变量就会被初始化或者赋值。Io_out8函数,CPU如果只与内存相连,则只能完成计算和存储的功能。但CPU还要对键盘输入有响应,要通过网卡从网络取得信息,等等。这些设备会和CPU胡同电信号,为了区别这些设备,要使用设备号码(port)。向设备发送电信号的是OUT指令;从设备取得电信号的是IN指令。但在C语言中没有与IN和OUT相当的语句,所以需要用汇编语言来做。而代码中的0x03c8、0x03c9就是设备

10、号。最后代码如下:void set_palette(int static, int end, unsigned char *rgb) int i, eflags; eflags = io_load_eflags(); /*记录中断许可标志的值*/ io_cli(); /*将中断许可标志置为0,禁止中断*/ io_out8(0x03c8, start); for (i = start; i = end; i+) io_out8(0x03c9, rgb0 / 4); io_out8(0x03c9, rgb1 / 4); io_out8(0x03c9, rgb2 / 4); rgb += 3; io

11、_store_eflags(eflags); /*复原中断许可标志*/ return;在调色板的访问步骤中的CLI指将中断标志置为0的指令,STI是将这个终端标志置为1的指令。本来上一次日志就想写这个EFLAGS的,这次又遇到了,就记下笔记吧。8086CPU的标志寄存器有16位,其中存储的信息通常被称为程序状态字(PSW),简称flag。flag和其他寄存器不一样,其他寄存器是用来存放数据的,都是整个寄存器具有一个含义,而flag寄存器是按位起作用的,也就是说,它的每一位都有专门的含义,记录特定的信息。中断处理结束之后需要恢复中断现场,所以需要记住最开始的中断标志,所以io_load_efla

12、gs读取最初的eflags值,io_store_eflags恢复原来的值。这些都需要用汇编语言来实现。而CPU中并没有MOV EAX, EFLAGS之类的指令,能够用来读写EFLAGS的只有PUSHFD(push flags double-word,将标志位的值按双字压入栈)和POPFD(pop flags double-word,按双字长将标志位从栈弹出)指令_io_load_eflags: ; int io_load_eflags(void); PUSHFD ; 指PUSH EFLAGS POP EAX RET_io_store_eflags: ; void io_store_eflags

13、(int eflags); MOV EAX, ESP+4 PUSH EAX POPFD ; 指POP EFLAGS RET5、绘制矩形调色板弄好以后就可以画画了。在当前画面模式中有320x200(=64000)个像素。假设左上点的坐标是(0,0),右下点的坐标是(319,199),那么像素坐标(x, y)对应的VRAM地址应该这样计算:0xa0000 + x + y*320最终bootpack.c如下:/*在下面使用函数前需要先声明函数,相当于告诉C编译器,有一個函数在別的文件里*/ void write_mem8(int addr, int data);void io_hlt(void);v

14、oid io_cli(void);void io_out8(int port, int data);int io_load_eflags(void);void io_store_eflags(int eflags);/*就算写在同一个源文件里,如果想在定义前使用,还是必须事先声明一下*/void init_palette(void);void set_palette(int start, int end, unsigned char *rgb);/*是函数声明卻不用 ,而用;,这表示的意思是:函数在別的文件中,你自己找一下吧!*/#define COL8_ 0#define COL8_FF00

15、00 1#define COL8_00FF00 2#define COL8_FFFF00 3#define COL8_0000FF 4#define COL8_FF00FF 5#define COL8_00FFFF 6#define COL8_FFFFFF 7#define COL8_C6C6C6 8#define COL8_ 9#define COL8_ 10#define COL8_ 11#define COL8_ 12#define COL8_ 13#define COL8_ 14#define COL8_ 15void HariMain(void) char *vram; int xs

16、ize, ysize; init_palette(); /*设定调色板*/ vram = (char *) 0xa0000; xsize = 320; ysize = 200; boxfill8(vram, xsize, COL8_, 0, 0, xsize - 1, ysize - 29); boxfill8(vram, xsize, COL8_C6C6C6, 0, ysize - 28, xsize - 1, ysize - 28); boxfill8(vram, xsize, COL8_FFFFFF, 0, ysize - 27, xsize - 1, ysize - 27); boxf

17、ill8(vram, xsize, COL8_C6C6C6, 0, ysize - 26, xsize - 1, ysize - 1); boxfill8(vram, xsize, COL8_FFFFFF, 3, ysize - 24, 59, ysize - 24); boxfill8(vram, xsize, COL8_FFFFFF, 2, ysize - 24, 2, ysize - 4); boxfill8(vram, xsize, COL8_, 3, ysize - 4, 59, ysize - 4); boxfill8(vram, xsize, COL8_, 59, ysize -

18、 23, 59, ysize - 5); boxfill8(vram, xsize, COL8_, 2, ysize - 3, 59, ysize - 3); boxfill8(vram, xsize, COL8_, 60, ysize - 24, 60, ysize - 3); boxfill8(vram, xsize, COL8_, xsize - 47, ysize - 24, xsize - 4, ysize - 24); boxfill8(vram, xsize, COL8_, xsize - 47, ysize - 23, xsize - 47, ysize - 4); boxfi

19、ll8(vram, xsize, COL8_FFFFFF, xsize - 47, ysize - 3, xsize - 4, ysize - 3); boxfill8(vram, xsize, COL8_FFFFFF, xsize - 3, ysize - 24, xsize - 3, ysize - 3); for(;) io_hlt(); /*执行naskfunc里的_io_hlt*/ void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1) int x,

20、y; for (y = y0; y = y1; y+) for (x = x0; x = x1; x+) vramy * xsize + x = c; return;void init_palette(void) /*table_rgb的声明*/ static unsigned char table_rgb16 * 3 = 0x00, 0x00, 0x00, /* 0:黑 */ 0xff, 0x00, 0x00, /* 1:亮红 */ 0x00, 0xff, 0x00, /* 2:亮绿 */ 0xff, 0xff, 0x00, /* 3:亮黄 */ 0x00, 0x00, 0xff, /* 4

21、:亮蓝 */ 0xff, 0x00, 0xff, /* 5:亮紫 */ 0x00, 0xff, 0xff, /* 6:浅亮蓝 */ 0xff, 0xff, 0xff, /* 7:白 */ 0xc6, 0xc6, 0xc6, /* 8:亮灰 */ 0x84, 0x00, 0x00, /* 9:暗红 */ 0x00, 0x84, 0x00, /* 10:暗绿 */ 0x84, 0x84, 0x00, /* 11:暗黄 */ 0x00, 0x00, 0x84, /* 12:暗青 */ 0x84, 0x00, 0x84, /* 13:暗紫 */ 0x00, 0x84, 0x84, /* 14:浅暗蓝

22、*/ 0x84, 0x84, 0x84 /* 15:暗灰 */ ; set_palette(0, 15, table_rgb); return; /*C语言中的static char语句只能用于数据,相当于汇编中的DB指令*/void set_palette(int start , int end, unsigned char *rgb) int i, eflags; eflags = io_load_eflags(); /*记录中断许可标志的值*/ io_cli(); /*将中断许可标志置为0,禁止中断*/ io_out8(0x03c8, start); for (i = start; i

23、= end; i+) io_out8(0x03c9, rgb0 / 4); io_out8(0x03c9, rgb1 / 4); io_out8(0x03c9, rgb2 / 4); rgb += 3; io_store_eflags(eflags); /*复原中断许可标志*/ return;运行结果:这里关键看这个函数boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)即是从坐标(x0,y0)到(x1,y1),循环使其改变颜色为c所指代的颜色。二、遇到的问题及解决方法

24、1、关于调色板的色调。老师说要理解for (i = start; i = end; i+) io_out8(0x03c9, rgb0 / 4); io_out8(0x03c9, rgb1 / 4); io_out8(0x03c9, rgb2 / 4); rgb += 3; 这段代码里为什么要除以4,一开始只是想弄明白到底怎么显示的没有去可以了解显示的效果如何。后来发现不理解它,只是认为它负责把代表颜色的数据传入端口,至于除以4应该是改变了颜色。后来做完实验后回过头修改这个代码,截图拿出来,分别是不除,/2,/8,/16,/64,/128的结果,/4的在上面了。不除/2/8/16/64/256看

25、到上面的变化,我想大概清楚了,这不是亮度改变了吗。于是我想到了关于颜色的一些知识,以前见到过的饱和度、色调啥的,估计就是这些,就到网上去查了。果然:电脑屏幕上的所有颜色,都由这红色绿色蓝色三种色光按照不同的比例混合而成的。一组红色绿色蓝色就是一个最小的显示单位。屏幕上的任何一个颜色都可以由一组RGB值来记录和表达。因此这红色绿色蓝色又称为三原色光,用英文表示就是R(red)、G(green)、B(blue)。在电脑中,RGB的所谓“多少”就是指亮度,并使用整数来表示。通常情况下,RGB各有256级亮度,用数字表示为从0、1、2.直到255。注意虽然数字最高是255,但0也是数值之一,因此共25

26、6级。很明显数值每三个确定一个颜色,分别代表红绿蓝的分配比例,分配的越多,代表的颜色就越深。这样就似乎明白作者为什么说只要16种颜色了。确实,16*16=256,我们只要修改这个/号除的数,就可以得到不同的颜色。就不进行除法操作那个来说,就是调色板的原色,而进行了除法操作的都是相应色调降低了的:色调是各种图像色彩模式下原色的明暗程度,级别范围从0到255,共256级色调。例如对灰度图像,当色调级别为255时,就是白色,当级别为0时,就是黑色,中间是各种程度不同的灰色。在RGB模式中,色调代表红、绿、蓝三种原色的明暗程度,对绿色就有淡绿、浅绿、深绿等不同的色调。 色调是指色彩外观的基本倾向。在明

27、度、纯度、色相这三个要素中,某种因素起主导作有用,可以称之为某种色调。实验的结果似乎是证明的原色的明暗程度的改变。下面这些,以后如果要变颜色可能会用:对比度: 对比度指不同颜色之间的差别。对比度越大,不同颜色之间的反差越大,即所谓黑白分明,对比度过大,图像就会显得很刺眼。对比度越小,不同颜色之间的反差就越小。亮度: 亮度指照射在景物或图像上光线的明暗程度。图像亮度增加时,就会显得耀眼或刺眼,亮度越小时,图像就会显得灰暗。色调: 色调是各种图像色彩模式下原色的明暗程度,级别范围从0到255,共256级色调。例如对灰度图像,当色调级别为255时,就是白色,当级别为0时,就是黑色,中间是各种程度不同

28、的灰色。在RGB模式中,色调代表红、绿、蓝三种原色的明暗程度,对绿色就有淡绿、浅绿、深绿等不同的色调。 色调是指色彩外观的基本倾向。在明度、纯度、色相这三个要素中,某种因素起主导作有用,可以称之为某种色调色相: 色相就是颜色,调整色相就是调整景物的颜色,例如,彩虹由红、橙、黄、绿、青、蓝、紫七色组成,那么它就有七种色相。顾名思义即各类色彩的相貌称谓,如大红、普蓝、柠檬黄等。色相是色彩的首要特征,是区别各种不同色彩的最准确的标准。事实上任何黑白灰以外的颜色都有色相的属性,而色相也就是由原色、间色和复色来构成的饱和度: 饱和度是指图像颜色的浓度。饱和度越高,颜色越饱满,即所谓的青翠欲滴的感觉。饱和

29、度越低,颜色就会显得越陈旧、惨淡,饱和度为0时,图像就为灰度图像。可以通过调整电视机的饱和度来进一步理解饱和度的概念。在图像处理中,常见的颜色模型包括HSB(色相、饱和度、亮度)、RGB(红色、绿色、蓝色)、CMYK(青色、品红、黄色、黑色)和CIE L*a*b*等,因此,相应的颜色模式也就有RGB、CMYK、Lab等。在HSB颜色模型中,色相、饱和度、亮度是对图像属性的基本描述。色相或者色调(Hue)是从物体反射或透过物体传播的颜色。在0到360的标准色轮上,按位置度量色相。通常情况下,色相由颜色名称标识,如红色、橙色或绿色。亮度(Brightness或者intensity)是颜色的相对明暗

30、程度,通常使用从0%(黑色)至100%(白色)的百分比来度量图像的色调通常是指图像的整体明暗度,例如,如果图像亮部像素较多的话,则图像整体上看起来较为明快。反之,如果图像中暗部像素较多的话,则图像整体上看起来较为昏暗。对于彩色图像而言,图像具有多个色调。通过调整不同颜色通道的色调,可对图像进行细微的调整。在图像处理的各种颜色模型中,HSB模型以人类对颜色的感觉为基础,描述了颜色的3种基本特性。三、程序设计创新点1、创新绘制的图案动态等待条程序运行效果如下:解释一下,就是,这个短蓝条会在这个灰条里左右移动,做一个等待的效果。关键代码如下:这里先是将全部像素变为白色(vrami2 = 7;)然后制

31、作长灰条和短蓝条的初始状态;再然后到while(1)死循环,这里是动态的设置了,原理就是让CPU不断执行空指令达到延时的效果,每一次延时让短蓝条前方一个坐标变蓝,后边变黑。到达顶点就反过来。当然以后这个延时我们可以用CPU运行速度大小代替,这样似乎更合“等待条”的情理。然后死循环改为键盘等IO指令或网络信号,这样就更棒了。四、实验心得体会这次不仅仅学习到了如何通过修改vram来显示界面,还对颜色色彩有了一些认识,发现这一门课其实真的隐藏了很多知识的,信息也是真的厉害,似乎什么都可以用0101来表达,颜色这种抽象的概念也愣是表达了出来。看来还有很长的路要走啊。这次课整体还是很简单的,只不过修改代码可能要花点心思,比如说画个圆啥的,还要会算法,虽然可以暴力,但是程序员知道有好方法又怎么会用暴力去花费资源呢。本来想用暴力写一个对话框和文字,再动态移动,翻了翻后面的内容是有文字显示的,就放弃了这个想法。等待后面的内容吧!专心-专注-专业

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 教案示例

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁