用C语言实现内存写入(harib01a)
在naskfunc.nas添加部分:
_write_mem8: ;
void write_mem8(int addr, int data);
MOV ECX,[ESP+4] ; [ESP+4]中存放的是地址,将其读入ECX
MOV AL,[ESP+8] ; [ESP+8]中存放的是数据,将其读入AL
MOV [ECX],AL
RET
比如说:write_mem8( 0x1234,0x56)相当于MOV BYTE[0x1234],0x56如果C语言中用到了write_mem8函数,就会跳转到_write_mem8,此时参数指定的数字就存放在内存里,分别是:
第一个数字的存放地址:[ESP + 4]
第二个数字的存放地址:[ESP + 8]
第三个数字的存放地址:[ESP + 12]
第四个数字的存放地址:[ESP + 16]
…
与C语言联合使用,能自由使用的只有EAX,ECX,EDX。增加了[INSTRSET “i486p”],意思是这个程序给486用,古老的CPU会把EAX寄存器名会误认为是标签而不是寄存器。
CPU家谱中到286为止CPU是16位,386以后CPU是32位。8086->80186->286->386->486->Pentium->PentiumPro->PentiumII->PentiumIII->Pentium4->…本次的bootpack.c内容:
void io_hlt(void);
void write_mem8(int addr, int data);
void HariMain(void){
int i; /* 变量声明:i是一个32位整数 */
for (i = 0xa0000; i <= 0xaffff; i++) {
write_mem8(i, 15); /* MOV BYTE [i],15 */
}
for (;;) {
io_hlt();
}
}
For循环中间条件可以省略,这样就是无限循环,命令窗口输入make run:
条纹图案
修改一下bootpack.c就可以了
for (i = 0xa0000; i <= 0xaffff; i++) {
write_mem8(i, i & 0x0f);
}
写入的值变成了i&0x0f,运用了与运算:
计算A OR B 时,每一位分别计算,AB对应的位只要有一位是1,结果就是1同理, A AND B运算只要有一位是0结果就是0,全1则1。异或计算时(A XOR B)同样每一位分别计算,AB对应位数值相同则为0,不同为1,异或运算实现数据的翻转。本次用的是“与”运算,将地址值与0x0f相与,第四位原封保留,高四位全部变成0,所以每16个像素翻转一次,出现了图案。
挑战指针
Write_mem3( i , i & 0x0f); 代替=> *i =i & 0x0f;
代替之后运行发现出错
相当于 MOV [ 0x1234], 0x56
出现类型错误,因为指定内存时,不知道是BYTE还是WORD,还是DWORD,只有在另一方也是寄存器的时候才能省略,其他情况下不能省略。
要告诉计算机我们用的是BYTE需要定义成
Char *p; //变量p是用于内存地址的专用变量声明这样的变量p,p内放入与i相同的值,然后执行
*p= i & 0x0f;这样编译器就可以成功识别p是地址专用变量,用来存放字符
- Char *p; short *p; int *p;
分别用于存放BYTE \WORD\DWORD类型的地址但是变量p都是四个字节的,因为p是记录地址的变量。这样修改之后再运行,会出现条纹图案但是有一行警告
“赋值语句没有经过类型转换,由整数生成指针”这里的p是char类,而i是int型的所以两个在赋值的时候需要转换一下:
P=(char *) i;
改成这样C编译器不会弹出警告
色号设定
我们使用的是320×200的8位彩色模式,也就是用6位十六进制数来指定颜色(0-255色号这里是0-15色号 )。调色板:由程序员随意指定0-255的数字所对应的颜色,若不做任何设定,那么0号颜色->#000000,15号颜色->#ffffff。作者给出这次实验使用16种颜色(编号0-15)就可以绘制一个操作系统模样的画面:
#000000:黑
#00ffff: 浅亮蓝
#000084:暗蓝
#ff0000: 亮红
#ffffff: 白
#840084:暗紫
#00ff00: 亮绿
#c6c6c6: 亮灰
#008484:浅暗蓝
#ffff00: 亮黄
#840000:暗红
#848484:暗灰
#0000ff: 亮蓝
#008400:暗绿
#ff00ff: 亮紫
#848400:暗黄
本次的bootpack.c:
void io_hlt(void);
void 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);
void HariMain(void){
int i; /* 声明变量。变量i是32位整数型 */
char *p; /* 变量p是BYTE [...]用的地址 */
init_palette(); /* 设定调色板 */
p = (char *) 0xa0000; /* 指定地址 */
for (i = 0; i <= 0xffff; i++) {
p[i] = i & 0x0f;
}
for (;;) {
io_hlt();
}
}
void init_palette(void){
static unsigned char table_rgb[16 * 3] = {/* static *//* unsigned */
0x00, 0x00, 0x00, /* 0:黑 */
0xff, 0x00, 0x00, /* 1:亮红 */
0x00, 0xff, 0x00, /* 2:亮绿 */
0xff, 0xff, 0x00, /* 3:亮黄 */
0x00, 0x00, 0xff, /* 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:暗灰 */
};
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 <= 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;
}
关于0x03c7、0x03c8、0x03c9设备号码的理解:
作者给出的网页中“video DA converter”记述调色板的访问顺序:
- 首先在一连串的访问中屏蔽中断(CLI)
- 将想要设定的调色板号码写入0x03c8,再按照R,G,B的顺序写入0x03c9,如果继续设定下一个调色板,则省略调色板号码,再按照RGB的顺序写入0x03c9即可
- 若想读取当前调色板状态,要将调色板的号码写入0x03c7,再从0x03c9读取3次,R G
- B的顺序读出,若继续读出下一个调色板,同样省略调色板号码的设定,按顺序读出
- 如果最初执行了CLI,那么最后要执行STI(还原中断标志位)CLI:将中断标志置为0,STI:将中断指令置为1Set_palette中要在设定调色板之前首先执行CLI,处理之后要恢复中断标志,所以上面的函数中要先记录初始的中断标志,设定之后再还原中断标志位。
EFLAGS寄存器(书本):
进位标志可以通过JC或者JNC等跳转指令来判断0还是1;但是对于中断标志,没有类似的指令,只能读入EFLAGS来检查第九位是0还是1。书本上只给了16位寄存器的内容,可以百度一下32位寄存器保存的值:
CF:(进位标志)=1算术操作最高位产生了进位或借位 =0 最高位无进位或借位
PF:(奇偶标志位)=1数据最低8位中1的个数为偶数 =0 数据最低8位中1的个数为奇数
AF:(辅助进位标志) =1 D3→D4位产生了进位或借位 =0 D3→D4位无进位或借位
ZF:(零标志) =1 操作结果为0 =0 结果不为05
SF:(符号标志) =1 结果最高位为1 =0 结果最高位为0
OF:(溢出标志) =1 此次运算发生了溢出 =0 无溢出
绘制矩阵
本次加入两个部分一个是矩阵函数,一个是调用矩阵函数显示三个矩阵:
主函数部分:加入三行显示矩阵的代码,boxfill8函数传的参数分别是(地址,x坐标的最大值,颜色,x坐标的起始值,y坐标的起始值,x坐标的结束值,y坐标的结束值),比如说下面的boxfill8(p, 320, COL8_FF0000, 20, 20, 120, 120);就可以表示为在p中的像素点x坐标取值范围(0-320),选定的是COL8_FF0000(对应上面的颜色表表示亮红色)亮红色,从点(20,20)开始到(120,120)成一个矩阵填充,如下测试所示。
void HariMain(void){ char *p; /* p变量的地址*/
init_palette(); /* 设置调色板 */
p = (char *) 0xa0000; /* 将地址赋值进去 */
boxfill8(p, 320, COL8_FF0000, 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();
}
}
下面是这个函数部分,我们要实现的是二维的一个坐标点,但实际上存储每个像素的地址是一个一维的地址空间,我们要利用下面的公式进行转换(具体在遇到的问题2中有详细理解),先循环用点画出行,再循环用行画出一个矩阵,这就是下面函数的意思。
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;
}
最后的结果图,可以自己改变颜色
1、 最后成果图最后显示的东西添加了很多显示部分,都是显示矩阵,但是有叠加和组合,这样就可以显示想要的东西,用编号来一一对应起来在图中标出:
① 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);