8、鼠标控制与32位模式切换

鼠标解读
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后,开中断。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值