鼠标解读
bootpack.c节选
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舍弃掉
{
/* 等待鼠标的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)
{
/* 等待鼠标的第三字节 */
mouse_dbuf[2] = i;
mouse_phase = 1;
/* 鼠标的3个字节都齐了,显示处来 */
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);
}
}
}
}
1、如果移动鼠标,08 中的0会在0~3的范围变化,如果仅仅移动鼠标,08中的8不变,如果按鼠标左键或右键,会在8~F变化
2、当鼠标左右移动时,00会变化
3、当鼠标上下移动是,01会变化
移动鼠标指针
bootpack.c节选
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个字节都齐了,显示处来 */
sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
if ((mdec.btn & 0x01) != 0) //如果第一位是1,说明按下左键
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); /* 描画鼠标 */
}
}
}
}
make run之后可以看到鼠标可以移动了。
通往32位模式之路
asmhead.nas
; haribote-os boot asm
; TAB=4
BOTPAK EQU 0x00280000 ; bootpack.hrb(512KB)的开始地址
DSKCAC EQU 0x00100000 ; 用于保存软盘内容(1440KB)的开始地址
DSKCAC0 EQU 0x00008000
; 有关BOOT_INFO
CYLS EQU 0x0ff0 ;设定启动区
LEDS EQU 0x0ff1 ;键盘上各种LED指示灯的状态
VMODE EQU 0x0ff2 ;有关颜色数目的信息
SCRNX EQU 0x0ff4 ;分辨率的x
SCRNY EQU 0x0ff6 ;分辨率的y
VRAM EQU 0x0ff8 ;图像缓冲区的开始地址
ORG 0xc200 ;程序要装载的地方
MOV AL,0x13 ;VGA显卡,32x00x8位颜色
MOV AH,0x00 ;设置AH=0x00,调用显卡BIOS函数,就可以切换显示模式了,AL中设置模式
INT 0x10
MOV BYTE [VMODE],8 ; 记录画面模式
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000
; 用BIOS取得键盘上各种LED指示灯的状态
MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL
; PIC关闭一切中断
; 根据AT兼容机的规格,如果要初始化PIC,
; 必须在CLT之前进行,否则有时会挂起
; 随后进行PIC的初始化
MOV AL,0xff
OUT 0x21,AL ; 禁止主PIC的全部中断
NOP ; 什么也不做,让CPU休息一个时钟的时间。如果连续执行OUT指令,有些机种会无法正常进行
OUT 0xa1,AL ; 禁止从PIC的全部中断
CLI ; 禁止CPU级别的中断
; 为了让CPU能够访问1MB以上的内存空间,设定A20GAE
CALL waitkbdout ; 等待KBC准备好
MOV AL,0xd1
OUT 0x64,AL
CALL waitkbdout
MOV AL,0xdf ; 0xdf是让A20GATE信号线变成ON的状态,使内存1MB以上的部分变成可使用状态
OUT 0x60,AL
CALL waitkbdout
; 切换到保护模式。应用程序既不能随意改变段的设定,也不能使用操作系统专用的段,操作系统受CPU保护。
[INSTRSET "i486p"] ; “想要使用486指令”的叙述。INSTRSET指令,是为了能使用386以后的LGDT,EAX,CR0等
LGDT [GDTR0] ; 设定临时GDT
MOV EAX,CR0 ; 将CR0这一特殊的32位寄存器的值代入EAX。CR0 control register0 只有操作系统可使用
AND EAX,0x7fffffff ; 设bit31为0(为了禁止分页)
OR EAX,0x00000001 ; 设bit0为1(为了切换到保护模式)
MOV CR0,EAX ; 完成模式转换
JMP pipelineflush ; 当CPU切换到保护模式后时,要马上执行JMP指令。因为切换到保护模式后,
;机器语言的解释要发生变化,为了加快指令的执行而采用了管道机制。
pipelineflush:
MOV AX,1*8 ; 可读写的段32bit
MOV DS,AX ; 进入保护模式后,除CS外,所有的段寄存器的值都从0x0000变成了0x0008,相当于gdt+1段
MOV ES,AX
MOV FS,AX
MOV GS,AX
MOV SS,AX
; bootpack的转送
; 相当于memcpy(bootpack, BOTPAK, 512*1024/4),将bootpack.hrb复制到0x00280000号地址
MOV ESI,bootpack ; 转送源
MOV EDI,BOTPAK ; 转送目的地
MOV ECX,512*1024/4 ; 传送数据的大小以双字节大小为单位,所以要/4
CALL memcpy
; 磁盘数据最终转送到它本来的位置去
; 首先从启动区开始
; 相当于memcpy(0x7c00, DSKCAC, 512/4)
MOV ESI,0x7c00 ; 转送源
MOV EDI,DSKCAC ; 转送目的地,0x00100000
MOV ECX,512/4
CALL memcpy
; 所有剩下的
; 相当于memcpy(DSKCAC0+512, DSKCAC+512, cyls*512*18*2/4-512/4)
MOV ESI,DSKCAC0+512 ; 转送源, 0x00008200
MOV EDI,DSKCAC+512 ; 转送目的地, 0x00100200
MOV ECX,0
MOV CL, BYTE [CYLS]
IMUL ECX,512*18*2/4 ; 从柱面数变成字节数. IMUL是整数乘法
SUB ECX,512/4 ; 减去IPL
CALL memcpy
; 必须由asmhead来完成的工作,至此全部完毕
; 以后就由bootpack来完成
; bootpack的启动。对bootpack.hrb的header部分进行解析,将执行所必需的数据传送过去。
MOV EBX,BOTPAK ; q
MOV ECX,[EBX+16]
ADD ECX,3 ; ECX += 3;
SHR ECX,2 ; ECX /= 4;
JZ skip ; 没有要传送的东西时
MOV ESI,[EBX+20] ; 转送源
ADD ESI,EBX
MOV EDI,[EBX+12] ; 转送目的地
CALL memcpy
skip:
MOV ESP,[EBX+12] ; 栈初始值
JMP DWORD 2*8:0x0000001b ; 将2*8代入到CS中,同时移动到0x1b号地址。
waitkbdout: ; 与wait_KBC_sendready相同
IN AL,0x64
AND AL,0x02
IN AL,0x60 ; 空读,为了清空数据接收缓冲区中的垃圾数据
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,就跳转到memcyp
RET
ALIGNB 16 ; 一直添加DBO,直到地址能被16整除的时候。如果标签GDT0的地址不是8的倍
数,向段
; 寄存器复制的MOV指令就会慢一些,所以加入了ALIGN指令
GDT0: ;GDT0是一种特定的GDT。0号是空区域,不能在那里定义段。
RESB 8 ; NULL selector
DW 0xffff,0x0000,0x9200,0x00cf ; 可以读写的段32bit。set_segmdesc(gdt+1, 0xffffffff,
0x00000000, AR_DATA32_RW)
DW 0xffff,0x0000,0x9a28,0x0047 ; 可以执行的段32bit(bootpack用)。set_segmdesc(gdt+1,
0xffffffff, 0x00000000, AR_DATA32_RW)
DW 0
GDTR0: ; 是LGDT指令,意思是通知GDT0,有了GDT
DW 8*3-1 ; 写入16位段上限
DD GDT0 ; 写入32位段起始地址
ALIGNB 16
bootpack:
HariMain节选
struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
char s[40], mcursor[256], keybuf[32], mousebuf[128];
int mx, my, i;
struct MOUSE_DEC mdec;
init_gdtidt(); //GDT和IDT的初始化
init_pic(); //PIC初始化
io_sti(); //仅执行STI命令,IF变为1,CPU接收来自外部设备的中断
fifo8_init(&keyfifo, 32, keybuf);
fifo8_init(&mousefifo, 128, mousebuf);
//PIC属于外部设备,用IN OUT指令进行操作
io_out8(PIC0_IMR, 0xf9); /* 开放PIC1和键盘中断(11111001) */
io_out8(PIC1_IMR, 0xef); /* 开放鼠标中断(11101111) */
init_keyboard(); //准备好鼠标控制电路
init_palette(); //设置调色板
init_screen(binfo->vram, binfo->scrnx, binfo->scrny); //设置背景
总结一下切换到32位模式过程
1、初始化PIC,屏蔽所有中断,防止模式切换的过程中发生中断。
2、设定A20GATE,使CPU能访问1M以上的内存空间。
3、切换模式,设定临时GDT,将CR0最低一位设为1。
4、执行JMP指令,因为模式切换后要马上执行JMP指令,跳转到piplineflush段中,将除了CS以外的所有段寄存器都加8,相当于gdt+1段,因为进入保护模式后,段寄存器的解释不再是16倍,而是能够使用GDT
5、bootpack传送。
6、在主函数中重新创建GDT和IDT,初始化PIC后,开中断。