MacOS环境-手写操作系统-11-建立中断机制

简介: 本文详细介绍了如何为内核建立中断机制,涉及8259A中断控制器的初始化、中断信号的传递过程以及中断描述符表的设置。通过汇编和C语言代码展示了如何处理中断,特别是键盘和鼠标中断,最后给出了编译和运行的步骤。摘要由CSDN通过智能技术生成

建立中断机制

1.简介

上一节,我们绘制了鼠标图案,遗憾的是,鼠标箭头是死的,动不了,要想让鼠标移动,我们需要为内核建立中断机制

当我们移动鼠标时,鼠标会给CPU发送信号,CPU接收到信号后,终止当前的运算,执行内核给定的代码以处理鼠标发送的信号

中断信号的发送机制

每一个8259A控制器有8根中断信号线 总共可以接入15个外设硬件


一般情况下 鼠标接入的是从8259A所对应的IRQ4这根信号线


鼠标发送信号时 先通过管线IRQ4将信号传递到从8259A


然后通过管线IRQ2传递到主8259A


最后信号再传递给CPU 键盘产生的中断通过主8259A的IRQ1管线向CPU发送信号

既然有硬件,那就需要对其初始化后才能使用,对硬件的控制我们前面已经说过,需要通过端口发送命令来完成,要配置这两个控制器,我们需要对指定端口发送1字节的数据,这个一字节(8 bit)的数据,我们称之为ICW(initialization control word).主8259A对应的端口地址是20h,21h, 从8259A对应的端口是A0h和A1h. 对端口发送数据时,顺序是定死的,不能违背:
1. 往端口20h(主片)或A0h(从片)发送ICW1
2. 往端口21h(主片)或A1h(从片)发送ICW2
3. 往端口21h(主片)或A1h(从片)发送ICW3
4. 往端口20h(主片)或A0h(从片)发送ICW4

(这里略过一些概念的东西·· 毕竟比较晦涩难懂 别睡着了)


2.代码控制

下面我们通过代码配置两个中断控制器:


(1)先向主8259A发生ICW1:

mov al, 011h
out 020h, al

011h 对应的二进制是00010001 对应ICW1的说明


由于ICW1[0]=1表示需要发送ICW4, ICW1[1] = 0 说明有级联8259A(我们买来的电脑都是级联的)

ICW1[2] =0 表示用8字节来设置中断向量号


ICW1[3]=0表示中断形式是边沿触发 ICW[4]必须设置为1


ICW[5,6,7]必须是0


(那是相当的晦涩了·· 看个大概就好!后面慢慢懂吧 都是硬件的部分 我不是很关心)


(下面很长一部分都是这些 刚兴趣的可以去看看:https://blog.csdn.net/tyler_download/article/details/52716839


如果你和我一样不敢兴趣 那就先和我一起往下走吧!


综合以上 我们得到了如下的代码:

init8259A:
     mov  al, 011h
     out  02h, al
     call io_delay
  
     out 0A0h, al
     call io_delay

     mov al, 020h
     out 021h, al
     call io_delay

     mov  al, 028h
     out  0A1h, al
     call io_delay

     mov  al, 004h
     out  021h, al
     call io_delay

     mov  al, 002h
     out  0A1h, al
     call io_delay

     mov  al, 003h
     out  021h, al
     call io_delay

     out  0A1h, al
     call io_delay

     mov  al, 11111101b ;允许键盘中断
     out  21h, al
     call io_delay

     mov  al, 11111111b
     out  0A1h, al
     call io_delay

     ret

上边的这段代码 就是汇编部分 调用硬件中断的代码


那二话不说 汇编有了 我们去上C语言:


前面我们处理了硬件如何发送信号的问题


接下来 我们来看看 CPU接收到信号后 如何执行内核指定的代码


要执行相应代码 CPU必须知道代码所在的内存位置 这个信息是通过中断描述符表来实现的


中断描述符的数据结构:


struct GATE_DESCRIPTOR {
  short offset_low;
  short selector;
  char dw_count;
  char attribute;
  short offset_high;
};


中断描述符跟前面说到的全局描述符类似 也是用于描述内存性质的


只不过它专门用于描述可执行代码所在的内存


offset_low 和 offset_high 合在一起作为中断函数在代码执行段中的偏移


selector 用来指向全局描述符表中的某个描述符 中断函数的代码就处于该描述符所指向的段中


dw_count设置为0 attribute设置为08Eh


如何在内核中加载中断描述符表:


;Gate selecotor, offset, DCount, Attr
%macro Gate 4
  dw  (%2 & 0FFFFh)
  dw  %1
  dw  (%3 & 1Fh) | ((%4 << 8) & 0FF00h)
  dw  ((%2>>16) & 0FFFFh)
%endmacro


上面汇编代码中 %2对应的是4字节的地址偏移


把地址偏移的低2字节放到中断门的前两字节


接下来的一字节是宏定义的第一个参数 是中断代码所在的代码段的全局描述符


第三行设置中断描述符的属性 当前写死为08Eh


最后一行设置中断代码偏移的高二字节


在内核代码里 当全局描述符表加载到CPU后 就是我们加载中断描述符表的时机了


首先初始化一个中断描述符:


LABEL_IDT:
%rep  255
    Gate  SelectorCode32, SpuriousHandler,0, DA_386IGate
%endrep
IdtLen  equ $ - LABEL_IDT
IdtPtr  dw  IdtLen - 1
        dd  0


上面代码中 通过指令%rep 255 重复定义255个中断描述符


这么说来 CPU其实可以支持255种中断 其中两个8259A芯片的15个中断信号就包含在255个中断中 SpuriousHandler是中断代码的入口 我们把255个中断的处理代码都设置成SpuriousHandler 也就是无论哪个中断发生,都调用这个函数来处理:


 

xor   eax, eax
     mov   ax,  ds
     shl   eax, 4
     add   eax, LABEL_IDT
     mov   dword [IdtPtr + 2], eax
     lidt  [IdtPtr]


上面代码跟以前我们加载全局描述符表是一样的 由于加载全局描述符时我们使用指令cli关闭了中断功能


因此我们需要回复中断功能 CPU才能相应来自8259A芯片的信号:

  [SECTION .s32]
     [BITS  32]
     LABEL_SEG_CODE32:
     ;initialize stack for c code
     mov  ax, SelectorStack
     mov  ss, ax
     mov  esp, TopOfStack

     mov  ax, SelectorVram
     mov  ds,  ax

     mov  ax, SelectorVideo
     mov  gs, ax

     sti
     %include "write_vga_desktop.asm"

     jmp  $

上面的代码通过运行指令sti 恢复中断功能


最后再看看SpuriousHandler的实现


_SpuriousHandler:
SpuriousHandler  equ _SpuriousHandler - $$
call intHandlerFromC
iretd


当点击键盘 引发中断时 _SpuriousHandler的代码被调用 它又调用了C模块实现的函数intHandlerFromC


我们看看C语言怎么实现intHandlerFromC的


void intHandlerFromC(char* esp) {
    char*vram = bootInfo.vgaRam;
    int xsize = bootInfo.screenX, ysize = bootInfo.screenY;
    boxfill8(vram, xsize, COL8_000000, 0,0,32*8 -1, 15);
    showString(vram, xsize, 0, 0, COL8_FFFFFF, "PS/2 keyboard"); 
    for (;;) {
        io_hlt();
    }
    show_char();
}


(虽然晦涩难懂 但是还要看一看的!不可直接略过的!)


3.编译运行

下面又到了编译的时候了···


编译C文件


i386-elf-gcc -m32 -fno-asynchronous-unwind-tables -s -c -o write_vga_desktop.o write_vga_desktop.c


反汇编o文件


./objconv -fnasm write_vga_desktop.o write_vga_desktop.asm


删除无用部分


修改kernel


%include "write_vga_desktop.asm"


修改boot(直接放大一点)直接读了20个扇区 肯定够用了

mov          AL,  20        ; AL 表示要练习读取几个扇区


编译boot

nasm -o boot.bat boot.asm


编译kernel

nasm -o kernel.bat kernel.asm

运行java 生成system.img

相关文章
|
存储 缓存 Unix
第九章 操作系统接口【操作系统】2
第九章 操作系统接口【操作系统】2
161 1
|
自然语言处理 安全 Windows
第九章 操作系统接口【操作系统】1
第九章 操作系统接口【操作系统】1
193 1
|
13小时前
|
存储 Java C语言
MacOS环境-手写操作系统-04-实模式进入保护模式
MacOS环境-手写操作系统-04-实模式进入保护模式
6 1
|
11小时前
|
iOS开发 MacOS
MacOS环境-手写操作系统-40-进程消息通讯 和 回车键处理
MacOS环境-手写操作系统-40-进程消息通讯 和 回车键处理
9 2
|
12小时前
|
缓存 Java iOS开发
MacOS环境-手写操作系统-13-鼠标中断
MacOS环境-手写操作系统-13-鼠标中断
8 1
|
12小时前
|
存储 算法 C语言
MacOS环境-手写操作系统-15-内核管理 检测可用内存
MacOS环境-手写操作系统-15-内核管理 检测可用内存
8 0
|
11小时前
|
iOS开发 MacOS
MacOS环境-手写操作系统-30-进程之间互相切换
MacOS环境-手写操作系统-30-进程之间互相切换
6 0
|
11小时前
|
存储 算法 调度
MacOS环境-手写操作系统-29-进程切换
MacOS环境-手写操作系统-29-进程切换
5 0
|
13小时前
|
Java C语言 iOS开发
MacOS环境-手写操作系统-01-最小操作系统
MacOS环境-手写操作系统-01-最小操作系统
5 0
|
10小时前
|
小程序 iOS开发 MacOS
MacOS环境-手写操作系统-44-运行简单的程序
MacOS环境-手写操作系统-44-运行简单的程序
7 0