[读书笔记]30 天自制操作系统 day8 鼠标控制与32位模式切换

来源:互联网 发布:福建顶点软件 垃圾 编辑:程序博客网 时间:2024/06/05 03:59

1. 鼠标解读

  1. 一个鼠标事件实际上会产生 3 个byte 的数据, 可以借助缓冲区 unsigned char mouse_dbuf[3], 对其数据进行输出显示
  2. 同时为了提高代码可读性, 采用 MOUSE_DEC 结构体对其进行封装
  3. 鼠标的三个字节数据保存在 buf[3] 中, 其中 buf[0] 的 低3 bit 表征鼠标的按键状态, buf[1], buf[2] 表征鼠标 x, y 的相对移动
  4. 鼠标移动代码:
void HariMain(void){    struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;    char s[40], mcursor[256], keybuf[32], mousebuf[128];    int mx, my, i;    struct MOUSE_DEC mdec;    init_gdtidt();    init_pic();    io_sti(); /* IDT/PICの初期化が終わったのでCPUの割り込み禁止を解除 */    fifo8_init(&keyfifo, 32, keybuf);    fifo8_init(&mousefifo, 128, mousebuf);    io_out8(PIC0_IMR, 0xf9); /* PIC1とキーボードを許可(11111001) */    io_out8(PIC1_IMR, 0xef); /* マウスを許可(11101111) */    init_keyboard();    init_palette();    init_screen8(binfo->vram, binfo->scrnx, binfo->scrny);    mx = (binfo->scrnx - 16) / 2; /* 画面中央になるように座標計算 */    my = (binfo->scrny - 28 - 16) / 2;    init_mouse_cursor8(mcursor, COL8_008484);    putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);    sprintf(s, "(%3d, %3d)", mx, my);    putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);    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バイト揃ったので表示 */                    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. 通往32bit模式之路

  1. 这里主要分析一下 asmhead.nas 的一些内容
  2. 代码如下:

    ; haribote-os boot asm; TAB=4BOTPAK  EQU     0x00280000      ; bootpackのロード先DSKCAC  EQU     0x00100000      ; ディスクキャッシュの場所DSKCAC0 EQU     0x00008000      ; ディスクキャッシュの場所(リアルモード); 设定基本信息的存放地址CYLS    EQU     0x0ff0          ; ブートセクタが設定するLEDS    EQU     0x0ff1VMODE   EQU     0x0ff2          ; 色数に関する情報。何ビットカラーか?SCRNX   EQU     0x0ff4          ; 解像度のXSCRNY   EQU     0x0ff6          ; 解像度のYVRAM    EQU     0x0ff8          ; グラフィックバッファの開始番地        ORG     0xc200          ; このプログラムがどこに読み込まれるのか; 设定显卡的显示模式, 并将相应参数写入到内存的相应位置           MOV     AL,0x13         ; VGAグラフィックス、320x200x8bitカラー        MOV     AH,0x00        INT     0x10        MOV     BYTE [VMODE],8  ; 画面モードをメモする(C言語が参照する)        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 状态        MOV     AL,0xff        OUT     0x21,AL        NOP                     ; OUT命令を連続させるとうまくいかない機種があるらしいので        OUT     0xa1,AL        CLI                     ; さらにCPUレベルでも割り込み禁止; 让 cpu 可以访问 1MB 以上的内存空间        CALL    waitkbdout        MOV     AL,0xd1        OUT     0x64,AL        CALL    waitkbdout        MOV     AL,0xdf         ; enable A20        OUT     0x60,AL        CALL    waitkbdout; 切换CPU 模式为 保护模式    [INSTRSET "i486p"]              ; 486の命令まで使いたいという記述        LGDT    [GDTR0]         ; 暫定GDTを設定        MOV     EAX,CR0        AND     EAX,0x7fffffff  ; bit31を0にする(ページング禁止のため)        OR      EAX,0x00000001  ; bit0を1にする(プロテクトモード移行のため)        MOV     CR0,EAX        JMP     pipelineflushpipelineflush:        MOV     AX,1*8          ;  読み書き可能セグメント32bit        MOV     DS,AX        MOV     ES,AX        MOV     FS,AX        MOV     GS,AX        MOV     SS,AX; 将 bootpack 的内容写入到 0x280000 位置处            MOV     ESI,bootpack    ; 転送元        MOV     EDI,BOTPAK      ; 転送先        MOV     ECX,512*1024/4        CALL    memcpy; 将各个部分(启动扇区, IPL)拷贝到1MB 内存后面           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  ; シリンダ数からバイト数/4に変換        SUB     ECX,512/4       ; IPLの分だけ差し引く        CALL    memcpy; asmheadでしなければいけないことは全部し終わったので、;   あとはbootpackに任せる; bootpackの起動        MOV     EBX,BOTPAK        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    memcpyskip:        MOV     ESP,[EBX+12]    ; スタック初期値        JMP     DWORD 2*8:0x0000001bwaitkbdout:        IN       AL,0x64        AND      AL,0x02        JNZ     waitkbdout      ; ANDの結果が0でなければwaitkbdoutへ        RETmemcpy:        MOV     EAX,[ESI]        ADD     ESI,4        MOV     [EDI],EAX        ADD     EDI,4        SUB     ECX,1        JNZ     memcpy          ; 引き算した結果が0でなければmemcpyへ        RET; memcpyはアドレスサイズプリフィクスを入れ忘れなければ、ストリング命令でも書ける        ALIGNB  16GDT0:        RESB    8               ; ヌルセレクタ        DW      0xffff,0x0000,0x9200,0x00cf ; 読み書き可能セグメント32bit        DW      0xffff,0x0000,0x9a28,0x0047 ; 実行可能セグメント32bit(bootpack用)        DW      0GDTR0:        DW      8*3-1        DD      GDT0        ALIGNB  16bootpack:   
  3. 基本流程
Created with Raphaël 2.1.0开始设置显卡的显示模式 设置PIC, 屏蔽所有中断扩展 cpu 可以使用的内存 1MB 以上空间设置 32 bit 保护模式将相应区域拷贝到对应的位置上(ie, 将整体的空间向后移动 1MB 以后的空间位置)跳转到 bootpack 代码处执行结束

3. 内存分布图

这里写图片描述

4. 程序流程

  1. 下面我们来梳理下程序的运行流程

    Created with Raphaël 2.1.0startipl10.nasasmhead.nasbootpack.cend

4.1 ipl10.nas

  1. 这部分主要负责先载入 启动扇区(0x7c00 ~ 0x7dff)
  2. 启动扇区载入完毕之后,将 IPL (软盘中除了启动扇区的那一块) 载入到 内存的 0x8200 ~ 0x34fff 区域(这里实际上只是载入了 10 个柱面 180kb)
  3. 跳转到 0xc200 处运行(这个地方实际就是 asmhead 的开始位置)

4.2 asmhead.nas

  1. 这部分的主要工作在之前已经提到过了
  2. 他将0x7c00 ~ 0x7dff 处的512byte 的启动扇区数据拷贝到 0x100000 处(1MB 空间开始位置)
  3. 然后将 0x8200 ~ 0x34fff 处的IPL 数据拷贝到 0x100200 处
  4. 同时, 将bootpack.hrb 的数据拷贝到0x280000位置处
  5. 同时跳转到 bootpack.hrb 处开始执行

4.3 bootpack.c

  1. 这部分实际就是我们 C 语言部分的操作, 用于处理键盘,鼠标中断响应, 图形绘制等操作
0 0