实验日期 实验项目
2020.10.22 第4天 C语言和画面显示的练习

文章目录

  • 一、实验主要内容
    • 1、 内容1 用C语言实现内存写入
    • 2、 内容2 条纹图案
    • 3、 内容3 挑战指针和指针的应用
    • 4、 内容4 色号设定
    • 5、 内容5 绘制矩形和系统界面
  • 二、遇到的问题及解决方法
    • 1、 描述问题1
  • 三、程序设计创新点
    • 1、 描述创新点1,关键代码及结果截图
  • 四、实验心得体会

一、实验主要内容

1、 内容1 用C语言实现内存写入

(1).内容概要

  • 实验内容:用C语言实现内存写入,即向指定地址中写入指定的数据。
  • 实验重点:理解写入的原理和函数参数传入内存地址中的位置。

使用汇编语言编写一个write_mem8函数来实现对指定地址中数据的写入,wirte_mem8函数的参数1是地址,参数2是写入该地址的数据值。调用函数时,会在内存中开辟函数栈空间,函数参数从栈顶指针esp+4的位置开始存储,有如下对应关系:

第一个参数的存放地址 ESP+4
第二个参数的存放地址 ESP+8
第三个参数的存放地址 ESP+12

另外,指定内存地址的地方,可以自由使用寄存器EAX,ECX,EDX,其他寄存器因为在C语言编译生成的机器语言中用于记忆非常重要的值,因此只能使用其值,不能改变其值。

(2).关键代码分析

naskfunc.nas代码分析
[FORMAT "WCOFF"]
[INSTRSET "i486p"]    ;告诉nask,程序是给486使用的
[BITS 32]
[FILE "naskfunc.nas"]     GLOBAL  _io_hlt ,_write_mem8;
[SECTION .text]
_write_mem8:  ;void write_mem8(int addr,int data)MOV ECX,[ESP+4]   ;[ESP+4]存放的是地址MOV AL ,[ESP+8]   ;[ESP+8]存放的是数据MOV [ECX],ALRET

wirte_mem8函数是使用汇编语言写的,将地址和写入数据存放到对应的寄存器中,再利用相应的间接寻址的方式把数据写入指定地址中去。之和的实验中会使用C语言直接对内存写入,现在先暂时用汇编实现。

bootpack.c代码分析
void io_hlt(void);
void write_mem8(int addr,int data);
void HariMain(void)
{int i;for(i=0xa0000;i<=0xaffff;i++)//0xa0000-0xaffff这64k大小的内存中用来设定颜色{write_mem8(i,15);//将第2个参数的值写入第1个参数表示的地址中}for(;;){io_hlt();}
}

这部分代码在for循环中调用write_mem8函数将64k大小的图形缓冲区中每个像素换成15,即白色所对应的像素数值。替换完成后,运行代码,显示屏就会变成白色。

2、 内容2 条纹图案

(1).内容概要

  • 实验内容:绘制条纹图案

为了改变显示在屏幕上的图案,绘制出不同的图案效果。可以使用C语言中的逻辑运算符去改变颜色的色号。图形数据处理方式总结如下:

和1相或(OR) 使特定位变1
和0相与(AND) 使特定位变0
和1相异或(XOR) 使特定位反转

(2).关键代码分析

Bootpack.c代码分析
void io_hlt(void);
void write_mem8(int addr,int data);
void HariMain(void)
{int i;for(i=0xa0000;i<=0xaffff;i++){write_mem8(i,i & 0x0f);//i和0x0f相与,得到16种不同的色号}for(;;){io_hlt();}
}

这部分代码主要是用来绘制条纹图案。将i和0x0f相与,将会是i的值从0—f开始循环,对应在画面上的效果就是0-f对应 16种颜色,按照列依次排列形成条纹图案。
代码实现的效果如下:

3、 内容3 挑战指针和指针的应用

(1).内容概要

  • 实验内容: 了解指针的概念以及用法;学会使用指针将数据写入内存,并能够完成相应图形的绘制。
  • 实验重点:掌握几种使用指针将数据写入内存的方法,并能够熟练地使用指针。

在汇编语言中,可以省略BYTE等的情况:源和目的操作数位数相同;源和目的操作数均是寄存器。当我们使用指针(地址的专用变量)时,可以相应用于BYTE,WORD类地址。对应关系如下:

char *p 用于BYTE类地址,1个字节
short *p 用于WORD类地址,2个字节
int *p 用于DWORD类地址,4个字节

引入指针后,对于MOV ECX,I;MOV BYTE [ECX], (I &0x0f)两句汇编语言可以用C语言替代,声明一个指针变量p,指针变量指针地址ECX,*p表示该地址中存放的值,使用赋值语句即可改变地址p中存放的数值。
下表是对将数据写入内存的几种方式的总结:

方式 用法(使用的语句)
汇编语言MOV指令 MOV ECX,地址;MOV AL,数据;MOV [ECX],AL
类型转换 *((char *) i)=数据,i为写入的位置
指针 i[p]=数据;p[i]=数据; *(p+i)=数据;这3种方式中p为写入的首地址,需要提前指定,i为相对于首地址的偏移量p=(char *)i , *p=数据

(2).关键代码分析

bootpack.c代码分析
void io_hlt(void);
void write_mem8(int addr,int data);
void HariMain(void)
{int i;char *p;for(i=0xa0000;i<=0xaffff;i++){p=(char *)i;//这样写是为了消除警告*p=i & 0x0f;}for(;;){io_hlt();}
}

这部分代码是使用指针向内存写入数据,实现之前的图形绘制。其中p=(char )i用到了类型转换。类型转换是改变数值类型的命令,一般不必每次都注意类型转换,但是这里的给指针变量赋值时需要先强制类型转换为对应类型,不然会存在警告,因为不是每个数值都可以用来作为内存地址。在for循环中内容也可以相应使用类型转换将数据写入内存的语句替换,如((char *) i)=i & 0x0f,或者使用指针的其他写法,如i[p]=i &0x0f,p[i]=i &0x0f, *(p+i)=i & 0x0f。

4、 内容4 色号设定

(1).内容概要

  • 实验内容: 设定相应的颜色,在8位彩色模式下,初始化调色板。

本次实验中使用320*200的8位颜色模式,色号使用8位(二进制)数表示,但是这种方式可以指定的颜色很少。一般指定颜色是采用#ffffff的方式,使用6位16进制的数,用2位16进制的数表示R,2位16进制的数表示G,2位16进制的数表示B,也就是RGB的色彩表示方法。实验中用到的0-15号码的颜色如下:

调色板的访问步骤:

 a.设置中断屏蔽b.将设定的调色板号码写入0x03c8,将颜色按照RGB的顺序写入0x03c9。当设定多个调色板号码时,可以省略调色板号码,直接写入颜色。c.如果想要读出调色板的状态,则首先将调色板号码写入0x03c7,在从0x03c9中依次读出RGB。d.取消中断屏蔽。

汇编指令:IN指令是从设备取得电气信号,OUT指令是向设备发送电信号。IN和OUT指令可以实现CPU和设备之间的端口通信。PUSHFD指令是将标志位按照双字节压入栈,即PUSH EFLAGS,POPFD指令是将标志位按照双字节弹出栈,即POP EFLAGS。

EFLAGS寄存器的介绍:EFLAGS 是一组用来存放进位标志和中断标志等标志的存储器。进位标志可以用JC或者JNC来判断进位标志是否为0.。对于中断标志,需要读入EFLAGS,检查第9位的情况,第9位为0,忽视中断请求,否则则是立即处理中断请求。

(2).关键代码分析

  • 和之前内容1,2,3部分相同的代码不做说明
  • 汇编代码部分
_io_cli: ; void io_cli(void);//设置中断位为0CLIRET
_io_sti:    ; void io_sti(void);//设置中断位为1STIRET
  • init_palette代码分析
void init_palette(void)
{static unsigned char table_rgb[16 * 3] = {//定义16种颜色的数组0x00, 0x00, 0x00,   /*  0:黒 */0xff, 0x00, 0x00, /*  1:亮红 */…0x84, 0x84, 0x84    /* 15:暗灰 */};set_palette(0, 15, table_rgb);//设置调色板return;/* C语言中static char 语句只能用于数据,相当于汇编中的DB指令*/
}

这部分代码是初始化调色板,首先定义一个16*3的二维数据,每行表示一种颜色,将0-15号颜色按照RGB的顺序调用set_palette写入调色板

  • set_palette代码分析
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 <= 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;
}

这部分代码是设定的颜色写入对应的设备号码中。首先使用io_load_sflags()函数记录中断许可标志,接着调用io_cli()函数设置中断屏蔽,将设定的调色板号码写入0x03c8,使用for循环从start到end按照RGB的顺序分别将end-start+1钟颜色写入0x03c9。最后使用io_store_aflags()取消中断屏蔽。

5、 内容5 绘制矩形和系统界面

(1).内容概要

  • 实验内容: 通过像素坐标绘制不同颜色,不同大小,不同位置的矩形。
  • 实验重点:掌握像素坐标和绘制图形之间的关系,即图像的坐标系;学会利用坐标关系画画。

(2).关键代码分析

#define COL8_000000      0
…
#define COL8_848484     15
采用宏定义不同的色号
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{int x, y;for (y = y0; y <= y1; y++) {for (x = x0; x <= x1; x++)vram[y * xsize + x] = c;}return;
}

这部分代码是绘制矩形的代码。输入4个坐标参数,分别表示矩形左上角的坐标和矩形右下角的坐标,从而确定整个矩形。y*xsize+x表示按照列的方式进行访问,vram[y * xsize + x] = c表示给坐标为(x,y)的像素设置值为c。

void HariMain(void)
{char *p; init_palette(); /*设定调色板*/p = (char *) 0xa0000; /* 颜色设定的首地址*/boxfill8(p, 320, COL8_FF0000,  20,  20, 120, 120);//绘制矩形左上角坐标为(20,20),右下角坐标为(120,120)boxfill8(p, 320, COL8_00FF00,  70,  50, 170, 150);boxfill8(p, 320, COL8_0000FF, 120,  80, 220, 180);for (;;) {io_hlt();}
}

这部分代码是绘制三个不同颜色矩形的代码。绘制系统界面的代码类型,关键在于去确定矩形的位置。坐标系中y轴正方向水平向下,x轴正方向水平向右。以下是对系统界面的绘制的分析。

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 - 47, ysize - 24, xsize -  4, ysize - 24);
boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 23, xsize - 47, ysize -  4);
boxfill8(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);
//右下角方框

两份代码绘制效果如下:

二、遇到的问题及解决方法

1、 描述问题1

  • 问题描述

教材第77页,set_palette函数中rgb[0]/4、rgb[1]/4、rgb[2]/4为什么要除以4?

  • 解决方法

起初原本的想法是除以4之后,是为了提高颜色的灰度。经过试验发现确实如此,当增大除数的时候,颜色显示会变得越来越暗,除以一个数相当于是进行右移操作,颜色的像素值会变小,对应的颜色也会逐渐加深,但是对于除以4的解释还是不够全面。后来通过同学的启示,从图形显示模式入手,查阅资料得到如下原因。

本次实验绘制图形是在3202008的八位彩色图形模式下进行的。通过查阅资料可以知道VGA指定调色板颜色时,一个颜色频道只有6位,而我们使用的24位颜色模式中8位表示R,8位表示G,8位表示B,已经超出了颜色频道的表示位数,所以需要舍弃2位。考虑到颜色偏差不能太大,选择右移操作舍弃低2位,对应的操作就是除以4。

三、程序设计创新点

1、 描述创新点1,关键代码及结果截图

  • 创新点1

运用本次所学的图形显示技术,绘制了一个黄色的小人(以下简称小黄人),通过循环控制坐标的变化,可以使小黄人的双臂摆动,向屏幕前的你打招呼。背景颜色更换为亮灰色。

  • 关键代码

矩形绘制代码和原代码一致
圆的绘制代码

圆弧的绘制代码和圆的绘制代码基本一致,不过因为是绘制圆弧,需要对x,y两个坐标进行位置的限制,使其绘制出的结果是一个弧形。

主体函数部分

使用ly和ry控制手上下挥动,m用来控制挥手速度,k用来控制抬手的高度

  • 结果截图



    小黄人挥手的动画效果通过3张图片展示。

四、实验心得体会

  • 本次实验是自制操作系统的第4天,实验主要涉及的内容C语言的画面显示练习。笔者循序渐进从汇编开始实现内存写入,再到后面的指针使用,一步步进阶,设定调色板,熟悉图形的坐标系,最终可以使用C语言来绘制出不同的图形,甚至是动画的效果。第一次感觉自制操作系统有点样子了,可以实现画面显示,动画效果。
  • 实验中涉及的知识点基本上都是之前学习过的,如函数调用的参数地址,返回值存放的寄存器,指针的使用等等,因此实验过程中比较顺利。本次实验的重点就是画面显示,对于一个图形画面,设计的关键就是去给对应位置的像素设置不同的颜色,从而达到画图的效果,但对于一些比较复杂的图像,需要编程的程序代码也会是很长很复杂的。

30天自制操作系统——第4天实验总结相关推荐

  1. 操作系统实验--30天自制操作系统第11天实验日志(第3次节点考核代码)

    一.实验主要内容 1. 内容1:鼠标显示问题 以前鼠标移动到边界就不能再继续移动了,原因如下: 把鼠标左上角坐标的横坐标的范围限制在320-16,纵坐标的范围限制在200-16之中 所以我们可以讲鼠标 ...

  2. 30天自制操作系统——第12-13天实验总结

    实验日期 实验项目 2020.12.17 第12,13天 定时器 文章目录 一.实验主要内容 1. 内容1 使用定时器 2. 内容2 计量时间 3. 内容3 超时功能 4. 内容4 使用多个定时器 5 ...

  3. 操作系统实验--30天自制操作系统第5天实验日志

    一.实验主要内容 1.内容1:接收启动信息 为了保证在以后改变画面模式后,系统仍能正常运行,我们需要将显存首地址,画面长宽等数据0xa0000,320,200存入asmhead.nas文件中定义的地址 ...

  4. 索骥馆-DIY操作系统之《30天自制操作系统》扫描版[PDF]

    内容简介: <30天自制操作系统>是一本兼具趣味性.实用性与学习性的操作系统图书.作者从计算机的构造.汇编语言.C语言开始解说,让读者在实践中掌握算法.在这本书的指导下,从零编写所有代码, ...

  5. 30天自制操作系统第三天

    操作系统实验日志3 学号 201708010402 姓名 徐冰娜 专业年级班级 实验日期 2019.9.28 实验项目 第3天:进入32位模式并导入C语言 智能1701 30天自制操作系统第三天 操作 ...

  6. 30天自制操作系统 - 取代软盘,用U盘写入引导扇区

    背景 作者是在2006年发布此书的(翻译版是在2012年在中国发售),当时可能还能找到带有软驱的计算机.但是时过境迁,现在已经不存在这种老古董东西了. 简单总结一下学习到第三天的一些更变. 更变 书籍 ...

  7. 30天自制操作系统——第2天

    操作系统实验日志 实验日期 2018/9/29 实验项目 第2天:汇编语言学习与Makefile入门 一.实验主要内容 (一)汇编 1.关键代码 运行结果: 2.相关汇编指令的解释 指令 格式 含义 ...

  8. 30天自制操作系统第五天

    操作系统实验日志5 第5天:结构体.文字显示与GDT/IDT初始化 30天自制操作系统第五天 操作系统实验日志5 一.实验主要内容 1. 内容1:接收启动信息 2. 内容2:使用结构体 3. 内容3: ...

  9. 为什么《30天自制操作系统》封面中的猫是两只尾巴

    刚刚在一社区,发了一贴,被指出一问题,询一高人,得一答案.这便是我没有关注到的封面上的那只猫,我想这也是很多读者没有关注到的.因为在我微博的200转发贴中,并没有人提到封面中的猫为何有两只尾巴.于是咨 ...

最新文章

  1. 如何定制一款12306抢票浏览器——实现自动查询和预订功能
  2. 2020 ACM / ICPC 济南 A Matrix Equation (高斯消元、乘法原理)
  3. linux 换行符_「linux」libevent demo详细分析(对比epoll)
  4. mysql $区别_mysql 与oracle区别
  5. 在VS2008.Net下使用WPF开发Web应用程序
  6. java编程彩球滑梯作弊_课内资源 - 基于C++的学生成绩管理系统
  7. Struts2之数据标签(一)
  8. 旗下首发双模5G+双挖孔全面屏!Redmi K30真机谍照曝光
  9. HDU 5387 Clock(分数类+模拟)
  10. php中的全局异常,tp5 API 自定义全局异常处理(中)
  11. 【毕设教程】如何使用单片机控制步进电机
  12. 意外找回CSDN老账号,记录记录新账号备忘
  13. 用计算机pol计算方位角,怎样用科学计算器算坐标方位角
  14. Java Holder 使用
  15. 程控交换机、电话交换机、什么是交换机、网络交换机
  16. thingsboard往kafka推送数据
  17. 基于U3D的VR开发教程:(2)Vive详解
  18. ADS2019学习笔记:三、SPICE model导入
  19. 廊坊通岭计算机学校校长,计算机科学与技术S021-S024班校友荣归母校为母校捐赠文化石...
  20. TK-StringVar

热门文章

  1. matlab二维色图,matlab二维矩阵用颜色图像显示 + 色bar位置
  2. Java、JSP酒店餐饮管理系统
  3. 鸿蒙用java虚拟机_漫画:趣解鸿蒙 OS 如何实现跨平台?
  4. oracle创建触发器的作用,Oracle触发器简介
  5. 七年级计算机与信息安全教案,苏教版七年级信息技术上册2.3《计算机与信息安全》教学课件1 (共18张PPT)...
  6. 为什么鼓励可以重塑一个职场人?
  7. JS逆向:【硬干货】手把手实战某条_signature参数破解(下)——WebSocket与JS函数
  8. Perl-用正则表达式进行匹配1
  9. 利用Opencv3.4.15 vs2017实现鼠标获取图像像素值坐标、RGB值、HSV值
  10. Python爬虫实战,pymongo模块,Python实现数据分析国外人为啥喜欢李子柒