您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 其它行业文档 > 30天自制操作系统第4天
操作系统实验日志学号20160810520姓名甘昆禄专业年级班级智能1601实验日期2018.10.17实验项目第4天:C语言与画面显示练习一、实验主要内容1、让C语言读写内存上次实验隐约知道画面显示与vram有关,但具体的对应关系未知。这次我们用C语言来改写vram看看。作者首先用了汇编语言辅助函数,用了简单的方法,便于我们理解。Naskfunc.nas中增加了一个函数可供C语言调用的函数_write_mem8,用于实现直接写入指定内存地址的语句。如果C语言中write_mem8(0x1234,0x56);语句,则动作上相当于汇编的MOVBYTE[0x1234],0x56。第一个数字在内存里的存放地址[ESP+4],下一个数字的存放就依次累加4。这里两个参数,一个地址,一个数据。汇编与C语言结合时能自由使用的寄存区只有EAX,ECX,EDX3个,其他寄存区用于记忆非常重要的值,只能读不能写。这段代码中还增加了INSTRSET指令,是用来告诉nask这个程序是给486使用的,不然会被默认解释成8086机器使用(偶尔使用)的标签(label)或者常数。C语言代码:voidio_hlt(void);voidwrite_mem8(intaddr,intdata);voidHariMain(void){inti;for(inti=0xa0000;i=0xaffff;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是地址,而*p是p指向地址的内容。使用*i=i*0x0f可直接将i*0x0f写入i指向的内存地址中。*i=i*0x0f对应汇编的MOV[i],(i*0x0f),但如果直接这样写就不清楚[i]到底是BYTE还是WORD还是DWORD。由于MOV指令的两个对象必须是相同字节长度,即同类型(BYTE/WORD/DWORD),除非另一方是寄存器才可以省略。同理,在使用指针时需要事先声明它的类型,即指针所指向内容的类型。chari是类似AL的1字节变量,shorti是类似于AX的2字节变量,inti是类似于EAX的4字节变量。charp;/用于BYTE类地址*/shortp;/用于WORD类地址*/intp;/用于DWORD类地址*/以上指针中的p都是4字节,因为p是用于记录地址的变量。在汇编语言中,地址也像ECX一样,用4字节的寄存器来指定,所以也是4字节。p=i;/带入地址/p=i&0x0f;/这可以替代write_mem8(i,i&0x0f)*/在执行makerun之后出现了“warning:assignmentmakespointerfromintegerwithoutacast”这句话。在C语言中,普通数值和表示内存地址的数值被认为是两种不同的东西。如果将普通整数值赋给内存地址变量就会有警告,可以在赋值的时候使用强制类型转换:p=(char*)i;/*注意i的类型要和p类型一样*/指针应用p=(char*)0xa0000;/*给地址变量赋值*/for(inti=0;i=0xffff;++i){*(p+i)=i&0x0f;}在声明p的时候给它赋值为写入内存的起始地址,之后i作为地址增量,由p+i来指定写入内存的地址。C语言中,*(p+i)还可以改写成p[i]这种形式:p=(char*)0xa0000;/*给地址变量赋值*/for(inti=0;i=0xffff;++i){p[i]=i&0x0f;}p[i]与*(p+i)意思相同,这两者的差距只有前者4个字符,后者6个字符。但是p[i]并不能说是数组,只是一个看起来像是数列的使用了地址变量的省略写法而已。加法运算可以交换顺序,于是(p+i)和(i+p),p[i]和i[p],a[2]和2[a]都是一个意思,这更能说明它们与数组没有关系。4、调色板这次我们使用的是320*200的8位颜色模式,只有256种颜色,而计算机表示表示颜色时,都是用#ffffff一类的数,就是RGB表示法,可以表示256*256*256种颜色。那么我们现在8位数怎么表示颜色呢,其实我们这次只用到了16种。给每种颜色编上号码,像这样就可以使用了。再根据作者讲解修改完bootpack.c中的代码后,作者以汇编的角度解说table_rgb的声明部分。RESB指令是“reservebyte”的略写预约字节,如果想要从当前位置向后空出3个字节来,并且填0,就可以用RESB3在RESB3前面加上地址就变成了:a:RESB3与C语言中的chara[3]一个意思。但是汇编中RESB的内容能够保证是0,但是C语言不能保证,因此需要在这个声明后加上“={…}”,还可以写上数据的初始值。如chara[3]={1,2,3};即Chara[3];a[0]=1;a[1]=2;a[2]=3;a是表示最初地址的数字,也就是说它被认为是指针。像上面两种声明方式,汇编时都是要用到赋值语句的,作者说这样很浪费字节。而在声明前加上static就可以将汇编的RESB指令代替成DB指令,这样它在内存中的存储位置就变了,并且未初始化的全局静态变量会被程序自动初始化为0。而且在程序运行之前,static变量就会被初始化或者赋值。Io_out8函数,CPU如果只与内存相连,则只能完成计算和存储的功能。但CPU还要对键盘输入有响应,要通过网卡从网络取得信息,等等。这些设备会和CPU胡同电信号,为了区别这些设备,要使用设备号码(port)。向设备发送电信号的是OUT指令;从设备取得电信号的是IN指令。但在C语言中没有与IN和OUT相当的语句,所以需要用汇编语言来做。而代码中的0x03c8、0x03c9就是设备号。最后代码如下:voidset_palette(intstatic,intend,unsignedchar*rgb){inti,eflags;eflags=io_load_eflags();/*记录中断许可标志的值*/io_cli();/*将中断许可标志置为0,禁止中断*/io_out8(0x03c8,start);for(i=start;i=end;i++){io_out8(0x03c9,rgb[0]/4);io_out8(0x03c9,rgb[1]/4);io_out8(0x03c9,rgb[2]/4);rgb+=3;}io_store_eflags(eflags);/*复原中断许可标志*/return;}在调色板的访问步骤中的CLI指将中断标志置为0的指令,STI是将这个终端标志置为1的指令。本来上一次日志就想写这个EFLAGS的,这次又遇到了,就记下笔记吧。8086CPU的标志寄存器有16位,其中存储的信息通常被称为程序状态字(PSW),简称flag。flag和其他寄存器不一样,其他寄存器是用来存放数据的,都是整个寄存器具有一个含义,而flag寄存器是按位起作用的,也就是说,它的每一位都有专门的含义,记录特定的信息。中断处理结束之后需要恢复中断现场,所以需要记住最开始的中断标志,所以io_load_eflags读取最初的eflags值,io_store_eflags恢复原来的值。这些都需要用汇编语言来实现。而CPU中并没有MOVEAX,EFLAGS之类的指令,能够用来读写EFLAGS的只有PUSHFD(pushflagsdouble-word,将标志位的值按双字压入栈)和POPFD(popflagsdouble-word,按双字长将标志位从栈弹出)指令_io_load_eflags:;intio_load_eflags(void);PUSHFD;指PUSHEFLAGSPOPEAXRET_io_store_eflags:;voidio_store_eflags(inteflags);MOVEAX,[ESP+4]PUSHEAXPOPFD;指POPEFLAGSRET5、绘制矩形调色板弄好以后就可以画画了。在当前画面模式中有320x200(=64000)个像素。假设左上点的坐标是(0,0),右下点的坐标是(319,199),那么像素坐标(x,y)对应的VRAM地址应该这样计算:0xa0000+x+y*320最终bootpack.c如下:/*在下面使用函数前需要先声明函数,相当于告诉C编译器,有一個函数在別的文件里*///voidwrite_mem8(intaddr,intdata);voidio_hlt(void);voidio_cli(void);voidio_out8(intport,intdata);intio_load_eflags(void);voidio_store_eflags(inteflags);/*就算写在同一个源文件里,如果想在定义前使用,还是必须事先声明一下*/voidinit_palette(void);voidset_palette(intstart,intend,unsignedchar*rgb);/*是函数声明卻不用{},而用;,这表示的意思是:函数在別的文件中,你自己找一下吧!*/#defineCOL8_0000000#defineCOL8_FF00001#defineCOL8_00FF002#defineCOL8_FFFF003#defineCOL8_0000FF4#defineCOL8_FF00FF5#defineCOL8_00FFFF6#defineCOL8_FFFFFF7#defineCOL8_C6C6C68#defineCOL8_8400009#defineCOL8_00840010#defineCOL8_84840011#defineCOL8_00008412#defineCOL8_84008413#defineCOL8_00848414#defineCOL8_84848415voidHariMain(void){char*vram;intxsize,ysize;init_palette();/*设定调色板*/vram=(char*)0xa0000;xsize=320;ysize=200;boxfill8(vram,xsize,COL8_008484,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);boxfill8(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_848484,3,ysize-4,59,ysize-4);boxfill8(vram,xsize,COL8_848484,59,ysize-23,59,ysize-5);boxfill8(vram,xsize,COL8_000000,2,ysize-3,59,ysize-3);boxfill8(vram,xsize,COL8_000000,60,ysize-24,60,ysize-3);boxfill8(vram,xsize,COL8_848484,xsize-
本文标题:30天自制操作系统第4天
链接地址:https://www.777doc.com/doc-6028459 .html