要最终能够让鼠标动起来,作者分了好几次课来讲解。
成功让鼠标动起来的课程链接:
系统内核的洪荒之力:挪动鼠标
跟着作者讲解的步骤和代码操作,确实能够做出同样的效果,鼠标能够动起来了,感觉还是非常神奇的。但是这个过程中,更多的只是依葫芦画瓢,对相关技术的机制依然不甚了解,作者在这方面的讲解也几乎为0。
但是先动起来,有个感性的认识,对于后续学习也是有帮助的。
本章的学习笔记就是对代码添加了一些注释:
write_vga_desktop.c
#define COL8_000000 0
#define COL8_FF0000 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_840000 9
#define COL8_008400 10
#define COL8_848400 11
#define COL8_000084 12
#define COL8_840084 13
#define COL8_008484 14
#define COL8_848484 15
#define PORT_KEYDAT 0x0060
#define PIC_OCW2 0x20
#define PIC1_OCW2 0xA0
void io_hlt(void);
void io_cli(void);
void io_sti(void);
void io_out(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);
void show_char(void);
void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram,int xsize, unsigned char c, int x, int y,
int x0, int y0);
struct BOOTINFO {
char* vgaRam;
short screenX, screenY;
};
void initBootInfo(struct BOOTINFO *pBootInfo);
extern char systemFont[16];
void showFont8(char *vram, int xsize, int x, int y, char c, char* font);
void showString(char* vram, int xsize, int x, int y, char color, unsigned char *s );
void putblock(char* vram, int vxsize, int pxsize,
int pysize, int px0, int py0, char* buf, int bxsize);
void init_mouse_cursor(char* mouse, char bc);
void intHandlerFromC(char* esp);
static char mcursor[256];
static struct BOOTINFO bootInfo;
static char keyval[6] = {'0', 'X', 0, 0, ' ' ,0};
struct FIFO8 {
unsigned char* buf;
int p, q, size, free, flags;
};
static struct FIFO8 keyinfo;
static struct FIFO8 mouseinfo;
static char keybuf[32];
static char mousebuf[128];
struct MOUSE_DEC {
unsigned char buf[3], phase;
int x, y, btn;
};
static struct MOUSE_DEC mdec;
void fifo8_init(struct FIFO8 *fifo, int size, unsigned char* buf);
int fifo8_put(struct FIFO8 *fifo, unsigned char data);
int fifo8_get(struct FIFO8 *fifo);
int fifo8_status(struct FIFO8 *fifo);
char charToHexVal(char c);
char* charToHexStr(unsigned char c);
void init_keyboard(void);
void enable_mouse(struct MOUSE_DEC *mdec);
void show_mouse_info();
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat);
static int mx = 0, my = 0;
static int xsize = 0, ysize = 0;
void CMain(void) {
initBootInfo(&bootInfo);
char*vram = bootInfo.vgaRam;
xsize = bootInfo.screenX, ysize = bootInfo.screenY;
//接收键盘数据的缓存队列,长度为32
fifo8_init(&keyinfo, 32, keybuf);
//接收鼠标数据的缓存队列,长度为128
fifo8_init(&mouseinfo, 128, mousebuf);
//初始化调色板
init_palette();
//初始化鼠标
//所以我觉得这个函数叫init_mouse更合适
init_keyboard();
//绘制桌面背景
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);
//鼠标初始位置
mx = (xsize - 16) / 2;
my = (ysize - 28 - 16) / 2;
//初始化存储鼠标形状颜色的数组(16*16)
init_mouse_cursor(mcursor, COL8_008484);
//绘制鼠标
putblock(vram, xsize, 16, 16, mx, my, mcursor, 16);
//关闭中断
//CLI: clear interupt
//STI: start interupt
io_sti();
//mdec:用于接收鼠标数据的结构体:
/*
struct MOUSE_DEC {
unsigned char buf[3], phase;
int x, y, btn;
};
static struct MOUSE_DEC mdec;
*/
enable_mouse(&mdec);
int data = 0;
for(;;) {
io_cli();
if (fifo8_status(&keyinfo) + fifo8_status(&mouseinfo) == 0) {
//暂停,等待中断
io_stihlt();
} else if(fifo8_status(&keyinfo) != 0){
//从队列中读取数据之前就打开中断不会有并发问题吗
io_sti();
data = fifo8_get(&keyinfo);
char* pStr = charToHexStr(data);
static int showPosX = 0;
static int showPosY = 0;
showString(vram, xsize, showPosX, showPosY, COL8_FFFFFF, pStr);
showPosX += 40;
if(showPosX>=xsize)
{
showPosX=0;
showPosY+=15;
}
} else if (fifo8_status(&mouseinfo) != 0) {
show_mouse_info();
}
}
}
void computeMousePosition(struct MOUSE_DEC* mdec) {
mx += mdec->x;
my += mdec->y;
if (mx < 0) {
mx = 0;
}
if (my < 0) {
my = 0;
}
if (mx > xsize - 16) {
mx = xsize - 16;
}
if (my > ysize - 16) {
my = ysize - 16;
}
}
void eraseMouse(char* vram) {
boxfill8(vram, xsize, COL8_008484, mx, my, mx+15, my+15);
}
void drawMouse(char* vram) {
putblock(vram, xsize, 16, 16, mx, my, mcursor, 16);
}
void show_mouse_info(void) {
char*vram = bootInfo.vgaRam;
unsigned char data = 0;
io_sti();
data = fifo8_get(&mouseinfo);
if (mouse_decode(&mdec, data) != 0) {
eraseMouse(vram);
computeMousePosition(&mdec);
drawMouse(vram);
}
}
void initBootInfo(struct BOOTINFO *pBootInfo) {
pBootInfo->vgaRam = (char*)0xa0000;
pBootInfo->screenX = 320;
pBootInfo->screenY = 200;
}
void showString(char* vram, int xsize, int x, int y, char color, unsigned char *s ) {
for (; *s != 0x00; s++) {
showFont8(vram, xsize, x, y,color, systemFont+ *s * 16);
x += 8;
}
}
void init_palette(void) {
static unsigned char table_rgb[16 *3] = {
0x00, 0x00, 0x00,
0xff, 0x00, 0x00,
0x00, 0xff, 0x00,
0xff, 0xff, 0x00,
0x00, 0x00, 0xff,
0xff, 0x00, 0xff,
0x00, 0xff, 0xff,
0xff, 0xff, 0xff,
0xc6, 0xc6, 0xc6,
0x84, 0x00, 0x00,
0x00, 0x84, 0x00,
0x84, 0x84, 0x00,
0x00, 0x00, 0x84,
0x84, 0x00, 0x84,
0x00, 0x84, 0x84,
0x84, 0x84, 0x84,
};
set_palette(0, 15, table_rgb);
return;
}
void set_palette(int start, int end, unsigned char* rgb) {
int i, eflags;
//保存寄存器的值
eflags = io_load_eflags();
io_cli();
io_out8(0x03c8, start); //set palette number
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;
}
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;
}
}
void showFont8(char *vram, int xsize, int x, int y, char c, char* font) {
int i;
char d;
for (i = 0; i < 16; i++) {
d = font[i];
if ((d & 0x80) != 0) {vram[(y+i)*xsize + x + 0] = c;}
if ((d & 0x40) != 0) {vram[(y+i)*xsize + x + 1] = c;}
if ((d & 0x20) != 0) {vram[(y+i)*xsize + x + 2] = c;}
if ((d & 0x10) != 0) {vram[(y+i)*xsize + x + 3] = c;}
if ((d & 0x08) != 0) {vram[(y+i)*xsize + x + 4] = c;}
if ((d & 0x04) != 0) {vram[(y+i)*xsize + x + 5] = c;}
if ((d & 0x02) != 0) {vram[(y+i)*xsize + x + 6] = c;}
if ((d & 0x01) != 0) {vram[(y+i)*xsize + x + 7] = c;}
}
}
/*
mouse:存储鼠标颜色的数组
bc:背景颜色(深青色)
*/
void init_mouse_cursor(char* mouse, char bc) {
static char cursor[16][16] = {
"**************..",
"*OOOOOOOOOOO*...",
"*OOOOOOOOOO*....",
"*OOOOOOOOO*.....",
"*OOOOOOOO*......",
"*OOOOOOO*.......",
"*OOOOOOO*.......",
"*OOOOOOOO*......",
"*OOOO**OOO*.....",
"*OOO*..*OOO*....",
"*OO*....*OOO*...",
"*O*......*OOO*..",
"**........*OOO*.",
"*..........*OOO*",
"............*OO*",
".............***"
};
int x, y;
for (y = 0; y < 16; y++) {
for (x = 0; x < 16; x++) {
if (cursor[y][x] == '*') {
mouse[y*16 + x] = COL8_000000;//黑色
}
if (cursor[y][x] == 'O') {
mouse[y*16 + x] = COL8_FFFFFF;//白色
}
if (cursor[y][x] == '.') {
mouse[y*16 + x] = bc;//背景色
}
}
}
}
/*putblock(vram, xsize, 16, 16, mx, my, mcursor, 16);
vram:显存起始位置
vxsize:屏幕宽度
pxsize:鼠标的宽度
pysize:鼠标的高度
px0:起始位置
py0:起始位置
buf:存储数组形状的数组
bxsize:鼠标的宽度
*/
void putblock(char* vram, int vxsize, int pxsize, int pysize, int px0, int py0, char* buf, int bxsize) {
int x, y;
for (y = 0; y < pysize; y++)
for (x = 0; x < pxsize; x++) {
vram[(py0+y) * vxsize + (px0+x)] = buf[y * bxsize + x];
}
}
void intHandlerFromC(char* esp) {
char*vram = bootInfo.vgaRam;
int xsize = bootInfo.screenX, ysize = bootInfo.screenY;
io_out8(PIC_OCW2, 0x20);
unsigned char data = 0;
data = io_in8(PORT_KEYDAT);
fifo8_put(&keyinfo, data);
return;
}
//将0-15转换为对应的16进制字符的ASCII码
//比如:
//0->48
//A->65
char charToHexVal(char c) {
if (c >= 10) {
return 'A' + c - 10;
}
return '0' + c;
}
//将char类型的值转换为16进制字符
//比如16转换为:0x10
//比如15转换为:ox0f
char* charToHexStr(unsigned char c) {
int i = 0;
char mod = c % 16;
keyval[3] = charToHexVal(mod);
c = c / 16;
keyval[2] = charToHexVal(c);
return keyval;
}
#define PORT_KEYDAT 0x0060
#define PORT_KEYSTA 0x0064
#define PORT_KEYCMD 0x0064
#define KEYSTA_SEND_NOTREADY 0x02
#define KEYCMD_WRITE_MODE 0x60
#define KBC_MODE 0x47
/*
鼠标电路对应的一个端口是 0x64, 通过读取这个端口的数据来检测鼠标电路的状态,内核会从这个端口读入一个字节的数据,如果该字节的第二个比特位为0,那表明鼠标电路可以接受来自内核的命令,因此,在给鼠标电路发送数据前,内核需要反复从0x64端口读取数据,并检测读到数据的第二个比特位,知道该比特位为0时,才着手发送控制信息
*/
void wait_KBC_sendready() {
for(;;) {
if ((io_in8(PORT_KEYSTA) & KEYSTA_SEND_NOTREADY) == 0) {
break;
}
}
}
/*
先等待0x64端口返回可写信号,然后继续向端口发送一个字节数据,这个字节数值是 0x60, 该数据让键盘电路进入数据接收状态。紧接着向端口0x60发送一个字节的数据0x47, 这个数据要求键盘电路启动鼠标模式,这样,鼠标硬件所产生的数据信息,都可以通过键盘电路端口0x60读到
*/
void init_keyboard(void) {
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);
wait_KBC_sendready();
io_out8(PORT_KEYDAT, KBC_MODE);
return;
}
#define KEYCMD_SENDTO_MOUSE 0xd4
#define MOUSECMD_ENABLE 0xf4
/*
1. 先向端口发送一个字节的数据,改数据的值是0xd4,完成这一步后,任何向端口0x60写入的数据都会被传送给鼠标
2. 再发送的数值为0xf4,这个数据会被键盘电路发送给鼠标,该数据的作用是对鼠标进行激活,鼠标一旦接收到该数据后,立马向CPU发送中断信号,如果这时候,我们设置好鼠标的中断处理函数的话,相关函数的代码就会被CPU执行。(设置中断的代码在kernel.asm中:LABEL_IDT)
*/
void enable_mouse(struct MOUSE_DEC* mdec) {
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
wait_KBC_sendready();
io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
mdec->phase = 0;
return;
}
void intHandlerForMouse(char* esp) {
unsigned char data;
io_out8(PIC1_OCW2, 0x20);
io_out8(PIC_OCW2, 0x20);
data = io_in8(PORT_KEYDAT);
fifo8_put(&mouseinfo, data);
}
//先入先出的缓冲队列的初始化
void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf) {
fifo->size = size;
fifo->buf = buf;
fifo->free = size;
fifo->flags = 0;
fifo->p = 0;
fifo->q = 0;
return ;
}
#define FLAGS_OVERRUN 0x0001
int fifo8_put(struct FIFO8 *fifo, unsigned char data) {
if (fifo->free ==0) {
fifo->flags |= FLAGS_OVERRUN;
return -1;
}
fifo->buf[fifo->p] = data;
fifo->p++;
if (fifo->p == fifo->size) {
fifo->p = 0;
}
fifo->free--;
return 0;
}
int fifo8_get(struct FIFO8 *fifo) {
int data;
if (fifo->free == fifo->size) {
return -1;
}
data = fifo->buf[fifo->q];
fifo->q++;
if (fifo->q == fifo->size) {
fifo->q = 0;
}
fifo->free++;
return data;
}
int fifo8_status(struct FIFO8 *fifo) {
return fifo->size - fifo->free;
}
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat) {
if (mdec->phase == 0) {
if (dat == 0xfa) {
mdec->phase = 1;
}
return 0;
}
if (mdec->phase == 1) {
if ((dat & 0xc8) == 0x08) {
mdec->buf[0] = dat;
mdec->phase = 2;
}
return 0;
}
if (mdec->phase == 2) {
mdec->buf[1] = dat;
mdec->phase = 3;
return 0;
}
if (mdec->phase == 3) {
mdec->buf[2] = dat;
mdec->phase = 1;
mdec->btn = mdec->buf[0] & 0x07;
mdec->x = mdec->buf[1];
mdec->y = mdec->buf[2];
if ((mdec->buf[0] & 0x10) != 0) {
mdec->x |= 0xffffff00;
}
if ((mdec->buf[0] & 0x20) != 0) {
mdec->y |= 0xffffff00;
}
mdec->y = -mdec->y;
return 1;
}
return -1;
}
编译,反汇编,生成kernel.bat的命令行语句,可以一次执行:
gcc -m32 -fno-asynchronous-unwind-tables -s -c -o write_vga_desktop.o write_vga_desktop.c
objconv -fnasm write_vga_desktop.o write_vga_desktop.asm
sed -i "/SECTION/d" write_vga_desktop.asm
sed -i "/global/d" write_vga_desktop.asm
sed -i "/extern /d" write_vga_desktop.asm
nasm -o kernel.bat kernel.asm
要在任意目录使用objconv,可以将objconv所在目录加入环境变量PATH中,类似:
PATH=$PATH:/root/workspace/OS
到日前为止,C代码已经500多行了,用notepad看起来已经比较吃力了。
推荐source Insight:
windows10下安装source insight 4.0(破解版)