「30天制作操作系统系列」5~8天C语言处理鼠标键盘与中断

这篇博客讲述了如何在30天制作操作系统系列中,使用C语言处理键盘和鼠标中断。作者介绍了C语言基础,如结构体和指针,并详细讲解了GDT和IDT的初始化,以及如何处理中断,包括键盘中断和鼠标中断。此外,还涉及了模块化、头文件、编译规则以及中断控制器(PIC)的初始化和中断开启。
摘要由CSDN通过智能技术生成

目录

image

14天的时候我们已经完成了启动代码的编写并且从16位实模式转换到了32位保护模式,如果不清楚可以看看《2021-05-11【30天制作操作系统系列】14天从汇编到C语言》这篇,同时也从汇编过渡到了C语言,后面的处理几乎都是用C语言,5~8天作者讲的是键盘与鼠标信号的处理

简而言之,要实现的功能就是按下键盘,屏幕上要显示相应的字符,移动鼠标,鼠标指针要进行移动

C语言基础

结构体

在OS启动的汇编里面定义了屏幕分辨率,在地址0x0ff4处写入一个十进制数320(2字节),在内存0x0ff6写入了一个十进制数200(2字节)

SCRNX   EQU     0x0ff4          ; 分辨率X
SCRNY   EQU     0x0ff6          ; 分辨率Y
...
MOV     WORD [SCRNX],320
MOV     WORD [SCRNY],200

在C语言里面要取这两个值怎么取呢,地址是确定的,当然是用指针,如下

short sizex,sizey;//定义值
short *scrnx,*scrny;//定义指针

//指针赋值,赋的是内存地址的值
scrnx = (short *) 0x0ff4;
scrny = (short *) 0x0ff6;

//内存地址所代表的值取出来
sizex = *scrnx;
sizey = *scrny;

// 还可以合到一起写
sizex = *( (short *) 0x0ff4 );

scrnx是地址,*scrnx就是该地址的值320,当然此处只有两个变量,那如果变量很多并且能够归纳为同一类,那么就可以用结构体,用struct定义

struct SOMEINFO {
    short scrnx, scrny;
}

short是2个字节,那么SOMEINFO一共就是四个字节,赋值的时候如下赋值

//定义值
short sizex,sizey;
//定义指针
struct SOMEINFO *info;
//指针赋值,赋变量开始位置的内存地址
info = (struct SOMEINFO *)0x0ff4;
//取出变量值
sizex = (*info).scrnx;
sizey = (*info).scrny;

还可以用箭头符号直接表示

sizex = info->scrnx;
sizey = info->scrny

作者是将启动信息封装到了结构体中,下面是汇编中定义的启动信息内存地址

; BOOT_INFO相关
CYLS    EQU     0x0ff0          ; 引导扇区设置
LEDS    EQU     0x0ff1
VMODE   EQU     0x0ff2          ; 关于颜色的信息
SCRNX   EQU     0x0ff4          ; 分辨率X
SCRNY   EQU     0x0ff6          ; 分辨率Y
VRAM    EQU     0x0ff8          ; 图像缓冲区的起始地址
;.... 一系列赋值操作
MOV     BYTE [VMODE],8  ; 屏幕的模式(参考C语言的引用)
MOV     WORD [SCRNX],320
MOV     WORD [SCRNY],200
MOV     DWORD [VRAM],0x 000a0000

C中结构体定义

//结构体
struct BOOTINFO {
    char cyls, leds, vmode, reserve;
    short scrnx, scrny;
    char *vram;
};

//赋值
struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
//取值
init_screen(binfo->vram, binfo->scrnx, binfo->scrny);

指针

这里再讲一下指针:特别要注意VRAM,它有点像一个嵌套指针,和SCRNX对比一下就知道差别了,SCRNX地址对应的内存里面装的是一个真正的值,而VRAM地址对应的内存里面装的是000a0000,它不是一个真正的值,它也是一个地址值,它对应的内存里面装的才是真正的值

image

指针的赋值

以下指针的赋值表示指针vram往后第i个地址的值为c

vram[i] = c;

完全等价于

*(vram+i) = c;

或者

*(i+vram) = c;

再或者

i[vram] = c

本质上讲,以上几种学法没有区别,因为编译后的汇编都是一样的!一定要注意指针和数组的区别,千万不要混淆,指针和数组没有半毛钱关系!

还需要注意一点的是指针的加法中蕴含着乘法运算,比如*(vram+1) = 100;与*(vram+2) = 200;并不表示每次内存地址只是移动了一个字节,因为vram指针是四个字节的,所以每次都会移动四个字节,如下图所示

image

模块化

所谓模块化就是对源文件进行逻辑归类而已,把含有相同功能的一些功能放到一个单独的.c文件里面,这样方便管理,在06_day里面,作者将原来冗长的bootpack.c分成了以下三大块

  • 处理图像处理的模块:graphic.c
  • 初始化GDT/IDT的模块:dsctbl.c
  • 主函数模块:bootpack.c

头文件

当然分了模块以后,各个模块定义的函数如果要相互引用,就要将他们放到.h头文件里面,所以有个bootpack.h,里面除了定义啥也没有,每个模块都会导入这个头文件,这样就可以引用别的模块甚至是汇编里面定义的函数了,关系如下所示

image

编译规则

但是分了模块以后,.c源文件就会增加,每增加一个都要新增一个编译规则吗?当然不是,编译规则也有通配符规则,如下,作者在Makefile里面年增加了通配符编译规则

%.gas : %.c Makefile # 将所有.c源代码文件编译成gas汇编
    $(CC1) -o $*.gas $*.c

%.nas : %.gas Makefile # 将所有gas汇编转换成nas汇编
    $(GAS2NASK) $*.gas $*.nas

%.obj : %.nas Makefile # 将所有nas汇编编译成了obj二进制
    $(NASK) $*.nas $*.obj $*.lst

GDT/IDT初始化

GDT(Global Descriptor Table)和IDT(Interrupt Descriptor Table)其实在《1~4天从汇编到C语言》已经提到过了,在16位实模式转换成32位保护模式的时候就已经初始化过GDT了,为啥此处还需要初始化一次&

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码狂魔v

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值