1.鼠标
1.1将鼠标三个数据显示出来
enable_mouse();
mouse_phase = 0; /* 进入等待鼠标的0xfa状态 */
for (;;) {
io_cli();
if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {
io_stihlt();
} else {
if (fifo8_status(&keyfifo) != 0) {
i = fifo8_get(&keyfifo);
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
} else if (fifo8_status(&mousefifo) != 0) {
i = fifo8_get(&mousefifo);
io_sti();
if (mouse_phase == 0) {
/* 等待鼠标的0xfa状态,并将其抛弃 */
if (i == 0xfa) {
mouse_phase = 1;
}
} else if (mouse_phase == 1) {
/* 将鼠标第一个字节保存 */
mouse_dbuf[0] = i;
mouse_phase = 2;
} else if (mouse_phase == 2) {
/* 将鼠标第二个字节保存 */
mouse_dbuf[1] = i;
mouse_phase = 3;
} else if (mouse_phase == 3) {
/* 将第三个字节保存,并重置状态为1 */
mouse_dbuf[2] = i;
mouse_phase = 1;
/* 将三个字节显示出来 */
sprintf(s, "%02X %02X %02X", mouse_dbuf[0], mouse_dbuf[1], mouse_dbuf[2]);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 8 * 8 - 1, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
}
}
}
}
其结果如图,08表示鼠标状态(如果移动鼠标,0会在0~3之间变化;如果点击鼠标,8会在8~F之间变化)。FD、01代表鼠标左右、上下坐标 。
1.2将程序拆分详解
struct MOUSE_DEC {
unsigned char buf[3], phase;
};
void enable_mouse(struct MOUSE_DEC *mdec);
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat);
void HariMain(void)
{
……
struct MOUSE_DEC mdec;
……
enable_mouse(&mdec);
for (;;) {
io_cli();
if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {
io_stihlt();
} else {
if (fifo8_status(&keyfifo) != 0) {
i = fifo8_get(&keyfifo);
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
} else if (fifo8_status(&mousefifo) != 0) {
i = fifo8_get(&mousefifo);
io_sti();
if (mouse_decode(&mdec, i) != 0) { /* 读取3个字节,返回0、1 */
/* 显示3个字节 */
sprintf(s, "%02X %02X %02X", mdec.buf[0], mdec.buf[1], mdec.buf[2]);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 8 * 8 - 1, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
}
}
}
}
}
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);
/* 顺利的化,ACK(0xfa)会被送过来 */
mdec->phase = 0; /* 等待0xfa阶段 */
return;
}
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat)
{
if (mdec->phase == 0) {
/* 等待0xfa阶段 */
if (dat == 0xfa) {
mdec->phase = 1;
}
return 0;
}
if (mdec->phase == 1) {
/* 等待鼠标第一个字节 */
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;
return 1;
}
return -1;
}
1.3对mouse_decode函数做进一步修改
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat)
{
if (mdec->phase == 0) {
/* 等待鼠标0xfa阶段 */
if (dat == 0xfa) {
mdec->phase = 1;
}
return 0;
}
if (mdec->phase == 1) {
/* 等待第一个字节 */
if ((dat & 0xc8) == 0x08) {
/* 判断鼠标状态的0、8两个数是否在0~3、8~F范围内 */
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]; /* x、y直接赋值 */
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; /* 人类常用坐标与窗口的坐标y值相反 */
return 1;
}
return -1;
}
if ((mdec->buf[0] & 0x10) != 0) {
mdec->x |= 0xffffff00;
}
if ((mdec->buf[0] & 0x20) != 0) {
mdec->y |= 0xffffff00;
}?
1.4鼠标图像移动
} else if (fifo8_status(&mousefifo) != 0) {
i = fifo8_get(&mousefifo);
io_sti();
if (mouse_decode(&mdec, i) != 0) {
/* 显示3字节 */
sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
if ((mdec.btn & 0x01) != 0) {
s[1] = 'L';
}
if ((mdec.btn & 0x02) != 0) {
s[3] = 'R';
}
if ((mdec.btn & 0x04) != 0) {
s[2] = 'C';
}
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
/* 鼠标指针移动,覆盖原来的鼠标 */
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, mx, my, mx + 15, my + 15);
/* 鼠标位置更新,加上位移量,并不能超过窗口 */
mx += mdec.x;
my += mdec.y;
if (mx < 0) {
mx = 0;
}
if (my < 0) {
my = 0;
}
if (mx > binfo->scrnx - 16) {
mx = binfo->scrnx - 16;
}
if (my > binfo->scrny - 16) {
my = binfo->scrny - 16;
}
sprintf(s, "(%3d, %3d)", mx, my);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 0, 79, 15); /* 隐藏旧坐标 */
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s); /* 显示新坐标 */
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16); /*描绘鼠标*/
}
2.讲解asmhead.nas
1.禁止中断
; PIC关闭中断
; 根据AT兼容机的规格,如果要初始化PIC
; 必须在CLI之前,否则会挂起
; 随后进行PIC的初始化
MOV AL,0xff
OUT 0x21,AL
NOP ; 让CPU休息一个时钟,如果连续OUT,有些几种会无法正常运行
OUT 0xa1,AL
CLI ; 禁止所有CPU级别的中断,必须在CLI之前,否则会挂起
2.设定A20GATE,使CPU能够访问1MB以上内存
; 设定A20GATE,使CPU能够访问1MB以上内存
CALL waitkbdout /*等同于wait_KBC_sendready*/
MOV AL,0xd1
OUT 0x64,AL
CALL waitkbdout
MOV AL,0xdf ; enable A20
OUT 0x60,AL /**/
CALL waitkbdout
程序向键盘控制电路发送指令——向键盘控制电路附属端口0x60(该端口连接着主板很多地方,向该端口发送不同指令,可以实现不同控制功能)输出0xdf(该指令让A20GATE信号线变为ON状态,之后CPU可以访问1MB以后内存。
3.切换保护模式
; 切换保护模式
[INSTRSET "i486p"] ; 想要使用486指令
LGDT [GDTR0] ; 设定临时GDT
MOV EAX,CR0 ; 32位寄存器CR0带入EAX
AND EAX,0x7fffffff ; 设bit31为0(禁止分页)
OR EAX,0x00000001 ; 设bit0为1(切换保护模式)
MOV CR0,EAX ; 设置32位寄存器CR0
JMP pipelineflush
pipelineflush:
MOV AX,1*8 ; 可读写的段 32bit
MOV DS,AX
MOV ES,AX
MOV FS,AX
MOV GS,AX
MOV SS,AX
保护模式中,分16位和32位(使用32位)。保护模式在,段寄存器不在是16倍,而是能使用GDT。除了CS意外所有段寄存器都从0x0000变成了0x0008,相当于“gdt+1”的段。
规定,通过带入CR0切换到保护模式时,要马上执行JMP指令。因为变成保护模式后,机器语言的解释发生变化。想要快点解释下一条指令。
4.转移数据到新内存
; bootpack的传送,在bootpack.hrb文件中,缝合后紧贴着asmhead.nas
MOV ESI,bootpack ; 转送源
MOV EDI,BOTPAK ; 目的地
MOV ECX,512*1024/4
CALL memcpy
;
; 首先从启动扇区开始
MOV ESI,0x7c00 ; 转送源
MOV EDI,DSKCAC ; 目的地
MOV ECX,512/4
CALL memcpy
; 巆傝慡晹
MOV ESI,DSKCAC0+512 ; 转送源
MOV EDI,DSKCAC+512 ; 目的地
MOV ECX,0
MOV CL,BYTE [CYLS] ; 柱面数
IMUL ECX,512*18*2/4 ; 转移字节数
SUB ECX,512/4 ; 减去启动扇区
CALL memcpy
memcpy(转送源地址,转速目的地址,转送数据大小)
5.bootpack的启动
; bootpack的启动
MOV EBX,BOTPAK
MOV ECX,[EBX+16]
ADD ECX,3 ; ECX += 3;
SHR ECX,2 ; ECX /= 4;右移两位
JZ skip ; 没有要转送的东西就跳转;等于0跳转
MOV ESI,[EBX+20] ; 转送源
ADD ESI,EBX
MOV EDI,[EBX+12] ; 目的地
CALL memcpy
skip:
MOV ESP,[EBX+12] ; 栈初始值,目的地地址
JMP DWORD 2*8:0x0000001b ;CS=2*8,0x1b值第二个段的0x1b地址
通过二进制编码器打开bootpack.hrb后可以看到这些值时多少。
纸娃娃操作系统内存分布:
6.waitkbdout和memcpy
waitkbdout:
IN AL,0x64
AND AL,0x02
IN AL,ox60 ;空读,清空数据接收缓冲区中的数据垃圾
JNZ waitkbdout ; AND结果如果不是0,就跳转到waitkbdout
RET
循环转移内存:
memcpy:
MOV EAX,[ESI]
ADD ESI,4
MOV [EDI],EAX
ADD EDI,4
SUB ECX,1
JNZ memcpy ; 减法运算如果不是0,跳转到memcpy
RET
7.?
ALIGNB 16
GDT0:
RESB 8 ; 僰儖僙儗僋僞
DW 0xffff,0x0000,0x9200,0x00cf ; 撉傒彂偒壜擻僙僌儊儞僩32bit
DW 0xffff,0x0000,0x9a28,0x0047 ; 幚峴壜擻僙僌儊儞僩32bit乮bootpack梡乯
DW 0
GDTR0:
DW 8*3-1
DD GDT0
ALIGNB 16
bootpack: