8259A

8259A

8259A是可编程中断控制器,通过与CPU的INTR引脚相连,向CPU传送外部中断。下图是来自于Intel的8259A的datasheet中的结构:


IR0 ~ IR7是外部中断源,所以每片8259A可以处理8个级别的中断向量,同时8259A可以串联使用,datasheet上说,不外加电路的情况下,通过串联可以实现64个级别的中断向量。不过实际使用中通常采用两片8259A串联的方式。下图来自于《x86/x64体系探索及编程》中关于串联的8259A:


每个8259A的IR引脚都和一条IRQ线链接,处理一个中断源,主片上的IR0 ~ IR7对应IRQ0 ~ IRQ7,从片上的IR0 ~ IR7对应IRQ8 ~ IRQ15,由于串联使用IR2,所以从片上的IR2同时对应IRQ2和IRQ9。这16个IRQ对应不同的硬件中断,具体请参考《IRQ》

8259A的IR引脚是有优先级的,编号越小的优先级越高,所以IR0最高,IR7最低,但是在级联的情况下,从片链接到了主片的IR2上这样就造成了中断源的优先级从高到低的顺序是:IRQ0, IRQ1, IRQ8 ~ IRQ15,IRQ3 ~ IRQ7。


中断响应

8259A内部有三种8位的寄存器,分别是中断请求状态寄存器IRR,中断服务状态寄存器ISR,中断屏蔽状态寄存器IMR。这三个寄存器都是8位的,每一位对应一个IRQ。

IRR

IRR寄存器是用来标记到达的中断请求的。当一个中断请求到达8259A的一个引脚的时候,对应的IRR上的位就被设置,表示该引脚上有一个中断到来了。例如IRQ0中断到达了,那么主片上的IRR的bit 0就被设置。如过IRQ10中断到达了,那么从片上的IRR的bit 2被设置。

ISR

ISR寄存器是用来记录被处理器处理的中断请求的。当一个中断请求到达并且被记录到IRR之后,8259A在适当的时候通知处理器产上了一个这样的中断,等待处理器处理,这个时候ISR对应的位就会被设置,IRR对应的位就被清除。例如8259A通知处理器有一个IRQ0中断等待处理,并且得到处理器允许,那么主片的ISR的bit 0被设置,同时IRR bit 0被清除。

IMR

IMR寄存器是用来记录屏蔽的中断请求的。当8259A打算屏蔽某一中断的时候就将IMR对应的位设置成1。例如打算屏蔽IRQ0,那么主片上的IMR bit 0被设置。当主片上的IMR bit 2被设置的时候,从片上的所有中断请求都被屏蔽。

中断处理过程

8259A的中断响应过程如下:

  1. 当某条IRQ线上发生了中断请求,对应的8259A设置IRR响应的位,表示发生了中断请求。
  2. 查看IMR是否屏别了该中断,如果没有屏蔽则给CPU发送INTR。
  3. CPU在接收到INTR之后会回复INTA。
  4. 当收到第一个INTA之后8259A进行优先级仲裁,优先级高的中断得到响应,设置响应的ISR位,并且清空对应的IRR位。
  5. 在收到第二个INTA之后将响应的中断向量通过数据总线传递给CPU。
  6. 如果是AEOI模式,在第二个INTA处理完成之后ISR对应的位自动清空,否则必须接收到一个EOI之后8259A才能清空对应的ISR位。


命令字

8259A的寄存器被成为命令字,分为初始化命令字ICW以及操作命令字OCW,每片8259A都含有4个ICW:ICW1 ~ ICW4,以及3个OCW:OCW1 ~ OCW3。顾名思义ICW就是用来初始化8259A的,OCW使用来操作8259A的。8259A中的寄存器采用I/O地址映射的方式,使用IN和OUT指令访问这些寄存器:

  1. 端口0x20:8259A主片的ICW1,OCW2以及OCW3。
  2. 端口0x21:8259A主片的ICW2 ~ ICW4以及OCW1。
  3. 端口0xa0:8259A从片的ICW1,OCW2以及OCW3。
  4. 端口0xa1:8259A从片的ICW2 ~ ICW4以及OCW1。

在使用8259A之前先要对其进行初始化,初始化的流程是分别写入主片和从片的ICW1 ~ ICW4。

ICW1

主片和从片的ICW1的格式是一样的,只是映射的I/O端口不同:


这是Intel 8259A datasheet上关于ICW1的结构,时间已经很久远了,现在有些位已经不是用了,例如D2和D3,必须都是0,还有D5~D7只在MCS-80/85模式下有效,现在也必须是0。

ICW2

ICW2使用来设置中断向量的,结构如下:


在8086/8088系统中只使用了D3~D7,D0~D2必须是0。D3~D7设置的是IRQ0中断向量的高5位,当前8259A上的其他IRQ的中断向量依次顺延。例如主片的ICW2的值是0x20那么IRQ0的中断向量就是0x20,IRQ1的中断向量就是0x21,以此类推,IRQ7的中断向量就是0x27。如果从片上的ICW2的值是0x28那么IRQ8的中断向量就是0x28,IRQ9就是0x29,以此类推IRQ15就是0x2f。

ICW3

ICW3在主片和从片上的结构是不同的,主片的结构:


主片中的每一位表示相应的IR是否链接了从片,例如只有IR2链接了从片,那么主片的ICW3的值就是0x04。

从片的结构:


从片的ICW3使用低3位表示从片标识代码(slave identification code),这个代码大体的意思应该是表示从片本身链接到主片的哪个IR上。例如从片链接到主片的IR2上时,从片的ICW3的值就是0x02。

ICW4

主片和从片的ICW4是一样的,使用来设置EOI模式以及嵌套模型等的:


SFNM是设置嵌套模式的,主要是用来处理从片上的中断,当SFNM=0的时候,从片上的中断会使主片的ISR的bit 2被置位,同时屏蔽后续从片上的优先级更高的中断。当SFNM=1的时候当主片的ISR的bit 2被置位时不会屏蔽从片上优先级更高的中断。

OCW1

OCW1对应的就是8259A中的IMR:


当主片上的OCW1的D2是1的时候,整个从片都被屏蔽。

OCW2

OCW2用来设置中断优先级以及发送EOI命令:


R,SL,EOI三位的组合值表示了不同的操作。L0~L2表示操作的IRQ值。

OCW3

OCW3有多种用途,可以用来读取IRR/ISR,设置poll模式或者Special Mask Mode:


Non-Special Mask Mode的时候当IRQ在处理的过程中ISR响应的位被设置,在发送EOI命令之前,8259A会屏蔽优先级低的IRQ。在Special Mask Mode中,当IRQ被响应的过程中,8259A不会查看ISR,直接根据IRR和IMR来处理中断,这样即使优先级低于正在被处理的IRQ的中断到达的时候,也会得到响应。


例子

通过一个简单的例子来了解一下如何对8259A进行编程,要控制8259A之前先要对其进行初始化:

###############################################################
# init 8259a
init_8259a:
    ## master 8259a
    # write ICW1
    movb $0x11, %al
    out %al, $MASTER_ICW1_PORT
    # write ICW2
    movb $0x20, %al
    out %al, $MASTER_ICW2_PORT
    # write ICW3
    movb $0x04, %al
    out %al, $MASTER_ICW3_PORT
    # write ICW4
    movb $0x01, %al
    out %al, $MASTER_ICW4_PORT
    ## slave 8259a
    # write ICW1
    movb $0x11, %al
    out %al, $SLAVE_ICW1_PORT
    # write ICW2
    movb $0x28, %al
    out %al, $SLAVE_ICW2_PORT
    # write ICW3
    movb $0x02, %al
    out %al, $SLAVE_ICW3_PORT
    # write ICW4
    movb $0x01, %al
    out %al, $SLAVE_ICW4_PORT
    ret
初始化就是按照主从片的顺序依次写ICW。

除了初始化8259A以外还要有中断处理函数,这里我们只处理了timer和keyboard中断,这两个中断的处理函数如下:

###############################################################
# keyboard_handler
keyboard_handler:
    jmp do_keyboard_handler
kmsg:   .asciz  ">>> now: enter keyboard handler"
kmsg1:  .asciz  "exit the keyboard handler<<<"
do_keyboard_handler:
    movl $kmsg, %esi
    call puts
    call println
    call dump_8259_imr
    call dump_8259_irr
    call dump_8259_isr
    movl $kmsg1, %esi
    call puts
    call println
    call println
    call write_master_EOI
    iret

###############################################################
# timer_handler
timer_handler:
    jmp do_timer_handler
tmsg:   .asciz  ">>> now: enter timer handler"
tmsg1:  .asciz  "exit the timer handler<<<"
do_timer_handler:
    movl $tmsg, %esi
    call puts
    call println
    call dump_8259_imr
    call dump_8259_irr
    call dump_8259_isr
    movl $tmsg1, %esi
    call puts
    call println
    call println
    call write_master_EOI
    call disable_timer
    call enable_keyboard
    iret
这样准备工作都完成了,下面就是在开启保护模式和分页之后调用上面的函数来操作8259A了:

    # set timer_handler and keyboard_handler
    movl $PIC8259A_TIMER_VECTOR, %esi
    movl $timer_handler, %edi
    call __set_interrupt_handler

    movl $KEYBOARD_VECTOR, %esi
    movl $keyboard_handler, %edi
    call __set_interrupt_handler

    call init_8259a

    call disable_timer
    call disable_keyboard

    sti

    movl $0xffffff, %ecx
l1:
    nop
    loop l1

    call dump_8259_imr
    call dump_8259_irr
    call dump_8259_isr
    call println

    call enable_timer

    jmp .
在安装了中断处理函数以及初始化了8259A之后,使用sti指令来开启中断。然后使用dump函数来打印出8259A各个寄存器的值。

sti指令允许cpu处理中断了,但是前面调用disable_keyboard让8259A来屏蔽keyboard中断,所以这个时候只有timer中断:


timer_handler执行过程中开启了keyboard中断,并且关闭了timer中断。这样在后续当窗口获得焦点的情况下按键盘任意键就会触发keyboard中断,并且执行keyboard_handler:



参考

《x86/x64体系探索及编程》

《8259A PROGRAMMABLE INTERRUPT CONTROLLER (8259A/8259A-2)》 ---- Intel 8259A datasheet

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值