Assembly 为什么不是';是否从8259执行IRQ0(8253定时器中断)的ISR?

Assembly 为什么不是';是否从8259执行IRQ0(8253定时器中断)的ISR?,assembly,x86,operating-system,interrupt-handling,Assembly,X86,Operating System,Interrupt Handling,是我的操作系统的源代码 我确保当CPU从8259 PIC(可编程中断控制器)获得中断时,它总是正确地“偏移”到ISR阵列(中断服务例程)。(您可以找到完整的代码。): 尚未实现的中断处理程序用\u irq\u unhandled表示。到目前为止,我发现CPU异常和软件中断(陷阱)工作正常。例如,当我的程序试图除以零时,它会跳到\u isr0。或者,当我尝试int1、int2、int7或任何类似操作时,isrs数组(IDT;中断描述符表)中的正确ISR被索引并调用。但是现在,我无法让PIC执行我的

是我的操作系统的源代码

我确保当CPU从8259 PIC(可编程中断控制器)获得中断时,它总是正确地“偏移”到ISR阵列(中断服务例程)。(您可以找到完整的代码。):

尚未实现的中断处理程序用
\u irq\u unhandled
表示。到目前为止,我发现CPU异常和软件中断(陷阱)工作正常。例如,当我的程序试图除以零时,它会跳到
\u isr0
。或者,当我尝试
int1
int2
int7
或任何类似操作时,
isrs
数组(IDT;中断描述符表)中的正确ISR被索引并调用。但是现在,我无法让PIC执行我的中断处理程序
\u isr32

_isr32:
  mov bl, 5
  mov bh, 15
  ; mov eax, MovCur
  ; and eax, 0xFFFF
  call MovCur

  mov eax, PicIntrMsg
  ; and eax, 0xFFFF  ; retrieve offset only when base address is something different than 0
  ; call 0x38:0x1008a
  call Puts32

  mov al, 0x20
  out 0x20, al
  ret
\u isr32
仅打印一条消息,表示已调用该消息,并将EOI(中断结束)消息发送回PIC。以下是启用PIC的例行程序,除计时器和键盘外,所有中断均被禁用:

%define IRQ_0   0x20                ; IRQs 0-7 mapped to use interrupts 0x20-0x27
%define IRQ_8   0x28                ; IRQs 8-15 mapped to use interrupts 0x28-0x36

; Initialization Control Word 1
%define ICW1_SEND_IC4 0x1
%define ICW1_SINGLE 0x2
%define ICW1_ADDRESS_INTERVAL_4 0x4 ; if set, use addresss inter, else 8
%define ICW1_LEVEL_TRIGGERED 0x8
%define ICW1_PIC_INITIALIZED 0x10
%define ICW1_IVT_ADDR1 0x20
%define ICW1_IVT_ADDR2 0x40
%define ICW1_IVT_ADDR3 0x80

; Initialization
; 1. write ICW1 to port 20h
; 2. write ICW2 to port 21h
; 3. if ICW1 bit D1=1  do nothing
; if ICW1 bit D1=0  write ICW3 to port 20h
; 4. write ICW4 to port 21h
; 5. OCW's can follow in any order
; http://stanislavs.org/helppc/8259.html
MapPIC:
  cli
  ; Setup ICW1
  mov al, (ICW1_SEND_IC4 | ICW1_PIC_INITIALIZED)
  out 0x20, al
  out 0xa0, al

  ; Setup ICW2
    ; send ICW 2 to primary PIC
  ; the first 31 interrupts (0x0-0x1F) are reserved
    mov al, IRQ_0       ; Primary PIC handled IRQ 0..7. IRQ 0 is now mapped to interrupt number 0x20
    out 0x21, al

    ; send ICW 2 to secondary controller
    mov al, IRQ_8       ; Secondary PIC handles IRQ's 8..15. IRQ 8 is now mapped to use interrupt 0x28
    out 0xa1, al

  ; Setup ICW3
  mov al, 0x4                   ; 0x4 = 0100 Second bit (IR Line 2)
  out 0x21, al  ; send to data register

  ; Send ICW 3 to secondary PIC
    mov al, 0x2     ; 0010=> IR line 2
    out 0xa1, al    ; write to data register of secondary PIC

  ; Setup ICW4
    mov al, 0x1     ; bit 0 enables 80x86 mode

    ; send ICW 4 to both primary and secondary PICs
    out 0x21, al
    out 0xA1, al

  ; All done. Null out the data registers
    mov al, 0
    out 0x21, al
    out 0xa1, al

  ; Disable all IRQs, except the timer and the keyboard
  mov al, 0xfc
  out 0x21, al
  out 0xA1, al

  ret
(完整的源代码在中)

检查Bochs的日志,IRQ0确实来自PIC以及键盘中断:

。。。
00030543642d[PIC]IRQ线0现在处于低位
00030543646d[PIC]IRQ线0现在处于高位
00030763342d[PIC]IRQ线0现在处于低位
00030763346d[PIC]IRQ线0现在处于高位
0003091600i[KBD]内部键盘缓冲区已满,忽略扫描码。(27)
00030983046d[PIC]IRQ线0现在处于低位
00030983050d[PIC]IRQ线0现在处于高位
00031048000i[KBD]内部键盘缓冲区已满,忽略扫描码。(26)
00031202746d[PIC]IRQ线0现在处于低位
00031202750d[PIC]IRQ线0现在处于高位
...
你可以查看完整的日志。根据日志,PIC已正确初始化:

00014765045d[PIC]主机:找到初始化命令1
00014765045d[PIC]要求4=1
00014765045d[PIC]级联模式:[0=级联,1=单个]0
00014765045d[PIC]主机:ICW1:选择边缘触发模式
00014765046d[PIC]IO写入00a0=11
00014765046d[PIC]从机:找到初始化命令1
00014765046d[PIC]要求4=1
00014765046d[PIC]级联模式:[0=级联,1=单个]0
00014765046d[PIC]从机:ICW1:已选择边缘触发模式
00014765048d[PIC]IO写入0021=20
00014765048d[PIC]主机:初始化命令2=20
00014765048d[PIC]偏移量=整数20
0001476500D[PIC]IO写入00a1=28
0001476500D[PIC]从机:初始化命令2=28
0001476500D[PIC]偏移量=整数28
00014765052d[PIC]IO写入0021=04
00014765052d[PIC]主机:初始化命令3=04
00014765054d[PIC]IO写入00a1=02
00014765054d[PIC]从机:初始化命令3=02
00014765056d[PIC]IO写入0021=01
00014765056d[PIC]主机:初始化命令4=01
00014765056d[PIC]正常EOI中断
00014765056d[PIC]80x86模式
00014765057d[PIC]IO写入00a1=01
00014765057d[PIC]从机:初始化命令4=01
00014765057d[PIC]正常EOI中断
00014765057d[PIC]80x86模式
00014765059d[PIC]IO写入0021=fb
00014765059d[PIC]将主PIC IMR设置为fb
00014765060d[PIC]IO写入00a1=fb
00014765060d[PIC]将从属PIC IMR设置为fb
00014765064d[PIC]IO写入0021=00
00014765064d[PIC]将主PIC IMR设置为00

尽管如此,它还是出了问题,我不知道我错过了什么。有人能帮我吗?

切换到用户空间会使我的操作系统无法再接收PIC的中断。如果不输入用户空间(即内核空间中的无限循环),操作系统可以很好地接收PIC中断。原来是这样。当我将
sti
放入系统调用条目(跳到例程取决于系统调用号)时,中断再次正常工作。在我的代码中,名为例程的程序比较
eax
中的syscall编号,并相应地跳转(将来我需要转换为函数指针数组)

此外,当第一次使用
iret
切换到用户空间时,
IF
位也被禁用,我需要设置IF位,并使用
pushf
将其放回EFLAGS寄存器,以便中断在用户空间中起作用

在相应地设置中断位后(通过
sti
或修改
EFLAGS
),我可以通过查看调用的ISR并使用如下顺序检查Bochs日志来验证中断是否工作:

....
04433276227d[PIC   ] IRQ line 0 now high
04433276227d[PIC   ] signalling IRQ(0)
04433277486d[PIC   ] IO write to 0020 = 20
04433372617d[PIC   ] IRQ line 0 now low
....

也就是说,IRQ0高,然后CPU响应0x20以确认中断,IRQ0再次低。

PIC初始化看起来确实正确。。。您确定需要“清空”数据寄存器吗?您是否初始化了
ss:esp
?@cad我尝试过不清空,但都是一样的。但是,输入ISR时,我没有设置
ss
。如您所见,我只设置了其他段寄存器。它重要吗?我应该将
ss
设置为什么值?目前,当发生异常(即除以0)时,我的操作系统可以输入中断,选择正确的ISR,执行并返回到以前的执行指令。是否缺少某些内容?可能我没有正确阅读此内容,但您的代码这样做是为了启用特定的中断
mov al,0xfb
out 0x21,al
out 0xA1,al
。0xfb=11111 011。因为您将该值写入从机和主机pic,它似乎在启用IRQ 2(在主机上)和IRQ 10(在从机上)。您不想启用INT 0和INT 1(都在master上)。这难道不需要将0xFC(11111100)写入端口0x21,将0xFF写入端口0xA1吗?@cad实际上,我已经设置好了,并且在进入阶段3时从未更改
ss
。你可以看到。每当输入ISR时,
ds
gs
。。。如您所见,已更改为内核数据段。我很高兴您对其进行了排序:)我讨厌8259。我做的第一件工作就是使用这个设备,我无法让它中断超过一个小时
Sysenter_Entry:
  sti   ; This solved the problem. VERY IMPORTANT.
  mov       bx, 0x10        ; set data segments to data selector (0x10)
  mov       ds, bx
    ; sysenter jumps here, is is executing this code at prividege level 0. Simular to Call Gates, normally we will
    ; provide a single entry point for all system calls.
  cmp eax, 0
  je clrscr
  cmp eax, 1
  je monitor_out
  cmp eax, 2
  je test_intr_kernel_space
  cmp eax, 3
  je test_intr_pic
  cmp eax, 4
  je STOP
  ; mov eax, GoodbyeMsg
  ; call Puts32
syscall_exit:
  ; restore back the stack for userspace afterward
  mov bx, 0x23
  mov ds, bx
  sysexit
....
04433276227d[PIC   ] IRQ line 0 now high
04433276227d[PIC   ] signalling IRQ(0)
04433277486d[PIC   ] IO write to 0020 = 20
04433372617d[PIC   ] IRQ line 0 now low
....