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

本文介绍了一个简单的操作系统环境下如何处理鼠标事件及其实现32位保护模式的方法。主要内容包括:鼠标的事件解析及屏幕上的移动,从实模式到保护模式的转换过程,以及相关内存布局。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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=4
    
    BOTPAK  EQU     0x00280000      ; bootpackのロード先
    DSKCAC  EQU     0x00100000      ; ディスクキャッシュの場所
    DSKCAC0 EQU     0x00008000      ; ディスクキャッシュの場所(リアルモード)
    
    ; 设定基本信息的存放地址
    CYLS    EQU     0x0ff0          ; ブートセクタが設定する
    LEDS    EQU     0x0ff1
    VMODE   EQU     0x0ff2          ; 色数に関する情報。何ビットカラーか?
    SCRNX   EQU     0x0ff4          ; 解像度のX
    SCRNY   EQU     0x0ff6          ; 解像度のY
    VRAM    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     pipelineflush
    pipelineflush:
            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    memcpy
    skip:
            MOV     ESP,[EBX+12]    ; スタック初期値
            JMP     DWORD 2*8:0x0000001b
    
    waitkbdout:
            IN       AL,0x64
            AND      AL,0x02
            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
    ; memcpyはアドレスサイズプリフィクスを入れ忘れなければ、ストリング命令でも書ける
    
            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:   
  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 语言部分的操作, 用于处理键盘,鼠标中断响应, 图形绘制等操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值