首页 > 其他分享 >第4天 c语言与画面显示的练习

第4天 c语言与画面显示的练习

时间:2023-05-22 20:24:14浏览次数:44  
标签:语言 int void 练习 0x00 0x84 画面 io 字节

用c语言实现内存写入

只显示黑乎乎的窗口一点意义也没有,我们需要值写入到现存中,以此来让显示器显示一些图像,首先利用汇编语言来定义一个函数,函数名称为_write_mem8,函数接收两个四字节的变量esp+4获取第一个变量的地址,esp+8获取第二个变量的地址,因为每个传过来的变量大小都是四字节,所有+ 4即可,然后将指定地址地址中保持到ecx中,将第二个变量保存到al中,将al值赋值到ecx中。
[INSTRSET 'i386p']表示汇编解析是利用486解析的,如果不指定的,默认为8086,8086使用的16位寄存器,没办法使用eax ecx esp这些32位寄存器。

[INSTRSET 'i386p']
GLOBAL _write_mem8

_write_mem8:
    mov ecx, [esp+4]
    mov al, [esp+8]
    mov [ecx], al
    ret

使用c语言,调用我们在汇编中定义的函数,循环在地址里赋值即可。

void io_hlt(void);

void write_mem8(int addr, int data);

void HariMain(void) {
    int i = 0xa0000;    
    for (; i <= 0xaffff; i++) {
        write_mem8(i, 15);
    }

    fin:
    io_hlt();

    goto fin;
}

这里直接使用指针不调用函数也是可以实现的。

void io_hlt(void);

void write_mem8(int addr, int data);

void HariMain(void) {
    int *p;
    int i = 0xa0000;    
    for (; i <= 0xaffff; i++) {
        p = i;
        *p = 15;
    }

    fin:
    io_hlt();

    goto fin;
}

运行结果如下,整个屏幕都会显示白色的,因为我们将显存地址全部写如了15,而15就代表白色。
img

条纹图案

以下语句也很好理解,就是循环地址为i的地方保存i与0x0f进行与运算值,将i和0xf转成二进制,两边同时进行与运算,如果两边为1结果为1,两边只要有一方为0,那么结果为0.

void io_hlt(void);

void write_mem8(int addr, int data);

void HariMain(void) {
    int i = 0xa0000;    
    for (; i <= 0xaffff; i++) {
        write_mem8(i, i & 0x0f);
    }

    fin:
    io_hlt();

    goto fin;
}

执行结果如下:
img

指针

指针上面例子以及演示过了,无非就是定义指针变量,将指针地址修改为i,然后直接操作指针地址的值为指定值。需要注意的是,以下汇编指令是没办法运行的

mov [0xf212], 21

原因是[0xf212]没有指定地址中存储的数据的自己大小是多少,这样的话将21保存到地址中就没办法保存了,因为21肯定是从地位赋值到高位的,字节大小不知道,肯定就没办法进行赋值了。
指针变量大小为四字节,因为指针变量中存储的就是32位地址,而32位地址刚好就是四字节,那么指针变量大小肯定就是四字节。
char是一字节,表示byte。
short是二字节,表示word。
int是字节表示,表示dword。

色号设定

之前调用bios中断0x19 al = 0x13 ah=00的画面设置为320*200 色号8位。
img
在8位色号中可以设置2^8=255个不同的颜色到调色板中。

void init_palette(void)
{
	// vag调色盘最多只能设置16种不同的颜色
	static unsigned char table_rgb[18 * 3] = {
		0x00, 0x00, 0x00, /*  0:黑 */
		0xff, 0x00, 0x00, /*  1:梁红 */
		0x00, 0xff, 0x00, /*  2:亮绿 */
		0xff, 0xff, 0x00, /*  3:亮黄 */
		0x99, 0xdd, 0xcc, /*  4:淡蓝 */
		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:浅暗蓝 */
		0x84, 0x84, 0x84, /* 15:暗灰 */
		0xb6, 0xa3, 0xbc, /* 16:dreamer鬃毛颜色 */
        0xFF, 0xC0, 0xCB,

	};

    set_palette(0, 18, table_rgb);
    return;
}

在定义RGB色号需要占用三个字节,书中如果不使用static进行赋值的话,占用内存是48 * 3 * 3个字节数,不知道是怎么得出来的,我觉得使用static和使用普通变量赋值的字节数都是相同的。。大概。上面定义了17个颜色数据,17 * 3 = 51个字节。使用static修饰的遍历在编译期就已经赋值了,
在c语言中如下定义static变量

static char a = 0x7f

那么在汇编中就会以以下方式进行赋值

a: 
    db 0x7f

普通局部变量存储在栈空间中的,函数调用完毕就会被释放掉了,而使用static修饰的变量是存储在静态存储区的,即便是函数执行完毕,变量依旧不会被释放。
如下所示,每次调用static都会修改变量内容
img
普通变量则是分配在栈中,每次调用函数都会重新分配
img

接着说设置调色盘的函数,如果cpu只连接内存而不连接其他设备的话,那么这台电脑只能由计算和存储功能,联网、展示图片什么的功能都没有,我们的电脑肯定不是这样的,因此cpu除了需要与存储设备连接还与其他设备连接,设备与cpu交互通过设备编码来进行交互,例如下面代码的0x03c9和0x03c8,cpu向设备发信息被称为out,cpu拉取设备发送的信息被称为in。
VGA文档信息:https://wiki.osdev.org/VGA_Hardware#VGA_Registers
我们根据设备编码向指定的设备发消息便可以自定义我们的调色盘。如下图所示,0x03c8是我们设置调色盘需要的设备编码。
img

设置调色盘的流程:

  1. 关中断,为了避免在初始化调色盘的过程中被其他服务将程序中断,首先将中断进行屏蔽。
    cli命令是关中断,将中断标志位设置成0.
  2. 以R、G、B的顺序将颜色写入到0x03c8端口中,如下所示这个函数就是在端口中写入调试盘序列,也就是第一个。
	io_out8(0x03c8, start);

接着按R、G、B的顺序将颜色写入到端口0x03c9中,如果想要接着继续设定调色盘,那么可以忽略0x03c8,直接设置0x03c9即可。
文档:https://moddingwiki.shikadi.net/wiki/VGA_Palette
img
io_out8(0x03c9, rgb[0] / 4);的原因是VGA红绿蓝三种颜色,每种颜色各占一个字节,且每个字节只能使用后低6位来表示颜色,值 / 4就相当于右移两位,虽然不知道为什么VGA只能使用6位来设置颜色,网上找资料也没找到,但是确实就是这么设置的。
如下FF二进制是 1111 1111
img
除4则是:
img
3. 设定调色盘完毕后打开中断。
sti命令是开中断,将中断标志位设置成1。当cpu遇到中断时会不会断下来是根据这个标志位来设定的。
io_load_eflags()函数的作用是在关中断前获取下中断标志位的值,在对调色盘设置完毕后再使用io_store_eflags()函数将中断位初始化回去。

void set_palette(int start, int end, unsigned char *rgb) 
{
	int i, eflags;
	// 记录中断寄存器中的值
	eflags = io_load_eflags();
	// 关中断
	io_cli();
	io_out8(0x03c8, start);

	for (i = start; i <= end; i++)
	{
		// 将调色板中记录的RGB值存储到0x03c9地址中
		// 为什么要 / 4 rgb 红绿蓝(R、G、B)都是一个具有 6 位值(从 0 到 63)的字节+ 两个 0)
		// 前两位0不识别 所以要右移两位使得前两位识别
		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;
}

PUSHFD是将标志位寄存器进行压栈
POPFD则是将标志位寄存器出栈
由于不能直接将标志位寄存器的值直接睡得着到eax寄存器中,所有需要先将标志位寄存器进行压栈,在出栈的时候在赋值给eax寄存器中。
stroe也是一样,先将eax寄存器进行压栈,出栈的时候再将标志位寄存器进行出栈操作。

; 
_io_cli:
    cli
    ret

_io_out8:
    mov edx, [esp+4]
    mov al, [esp+8]
    out dx, al
    ret

_io_store_eflags:
    mov eax, [esp + 4]
    push eax
    popfd
    ret

_io_load_eflags:
    pushfd
    pop eax
    ret

画矩形

之前设置的显示模式是320 * 200,横为320是像素,宽为200像素,左上角为0,0的像素的0,下一行1,0则是320,因为每一行320哥像素,Vrarm的算法就是0xa0000 + 320 * y + x。如下面这个方法。

void boxfill8(unsigned char *vram, int xSize, unsigned int color, int x0, int y0, int x1, int y1) {
	int x, y;

	for (x = x0; x <= x1; x++) {
		for (y = y0; y <= y1; y++) {
			*(vram + xSize * y + x) = color;
		}
	}
}

这个方法遍历x到x0,y到y0所有的像素点,并设置我们调色盘设置的指定颜色进行展示。
调用展示如下:

void HariMain(void)
{
    int j = 0;
    int i = 0xa0000;
    init_palette();

	char *p;

	p = (char *) 0xa0000;

	boxfill8(i, 320, 0xf, 70, 150, 120, 172);


fin:
    io_hlt();

    goto fin;
}

img

标签:语言,int,void,练习,0x00,0x84,画面,io,字节
From: https://www.cnblogs.com/lyraHeartstrings/p/17373069.html

相关文章

  • c++打卡练习(36)
    求多项式的和以50为例S=1+1/2+1/2*3+1/2*3*4+......1/2*3*.....*50流程图:伪代码:源代码:#include<iostream>usingnamespacestd;intmain(){ doublea=1,b,num,N; cout<<"输入你想阶乘到的最大数"<<endl; cin>>N; for(inti=1;i<=N;i++){ a*=i; b=1/a; num......
  • R语言学习记录
    2021年买的课程,今天终于开始正式学习了,这个故事告诉我们,学术还是得硕博时期搞,一旦毕业工作了,杂七杂八的事情太多,再加上家庭等因素,就会导致一拖再拖。首先,第一件事,放下焦虑,什么时候我变成了特别想一下子学会某件事情,而不再享受学习过程逐渐习得的快乐了呢?老师说,需要100小时才算是......
  • C语言 加入16进制格式 编译日期 编译时间
      要在C语言中打印16进制格式的编译日期和时间,可以这样实现:#include<stdio.h>intmain(){printf("Thisprogramwascompiledon0x%xat0x%x.\n",__DATE__,__TIME__);return0;} __DATE__和__TIME__都是以十进制格式定义的,我们在打印时使用0x%......
  • go语言变量定义及类型
    变量变量:赋值后,可以改变值的标识符。建议采用驼峰命名法。vara//错误,无法推测类型varbint//正确,只声明,会自动赋为该类型的零值varc,dint//正确,声明连续的同类型变量,可以一并声明,会自动赋为该类型的零值varb=200//错误,b多次声明,第二行已经声明过了//......
  • 页面置换算法的c语言实现
    #include<bits/stdc++.h>usingnamespacestd;intn;//物理块号数intlen,op;//进程数inta[100];//存储进程执行的先后顺序;intres[100][100];//存放进程执行的结果数组intoptfind[100],optflag[100];intlruflag[1000];intnru_value[100],nru_r[100],nru_m[100];voidprint......
  • 2023语言与智能技术竞赛开辟“双赛道”:寻找“全民测评官”,探索AI多模态能力
    开年以来,人工智能大语言模型(LLM)掀起新一轮全球科技竞赛,全球科技巨头打响“百模大战”。当大语言模型正深刻改变人类生产生活方式时,该如何进一步释放其潜能,成为业界关注的问题,也成为了2023语言与智能技术竞赛命题的起点。5月17日,2023语言与智能技术竞赛正式启动,该大赛由中国计算机学......
  • 打卡 c语言趣味编程 舍罕王的失算
    问题描述:相传国际象棋是古印度舍罕王的宰相达依尔发明的。舍罕王十分喜爱象棋,决定让宰相自己选择何种赏赐。这位聪明的宰相指着8×8共64格的象棋棋盘说:陛下,请您赏给我一些麦子吧。就在棋盘的第1格中放1粒,第2格放2粒,第3格放4粒,以后每一格都比前一格增加一倍,依此放完棋盘上64格......
  • Vue购物车实例练习
    功能介绍金额=单价*数量金额会自动根据数量的变化进行变化,我们可以点击按钮增加或减少商品的数量。合计金额:只有在序号列号勾选上才会被计入总金额中,金额总数会根据用户的操作自动更新数据。删除:如图我们勾选了第2个商品,当我们点击删除时,只会删除被选中的产品。全选:点击......
  • Python 1-24 练习五 综合练习
    1、无重复字符的最长子串给定一个字符串,请你找出其中不含有重复字符的最长子串的长度。#substr向右扩展一个元素,如果该元素已经在substr中,则需要将其及其前面的元素去掉。#可通过substr.index(c)定位元素或substr.split(c)[1]分割成子串#发现有重复字符时,可......
  • Python 1-11 练习一
    Python1-11练习一一、已知字符串s=“aAsmr3idd4bgs7Dlsf9eAF”,要求如下1、请将s字符串的大写改为小写,小写改为大写。#使用字符串的内置方法a.swapcase():s='aAsmr3idd4bgs7Dlsf9eAF't=s.swapcase()print(t)2、请将s字符串的数字取出,并输出成一个新的字符串。s=......