首页 > 解决方案 > 可编程中断控制器:PIC1 和 PIC2 在实模式下不返回零

问题描述

Hyper-V 输出:

在此处输入图像描述

代码:

/*PIC Definition*/
#define PIC1        0x20        /* IO base address for master PIC */
#define PIC2        0xA0        /* IO base address for slave PIC */
#define PIC1_COMMAND    PIC1
#define PIC1_DATA   (PIC1+1)
#define PIC2_COMMAND    PIC2
#define PIC2_DATA   (PIC2+1)




#define ICW1_ICW4   0x01        /* ICW4 (not) needed */
#define ICW1_SINGLE 0x02        /* Single (cascade) mode */
#define ICW1_INTERVAL4  0x04        /* Call address interval 4 (8) */
#define ICW1_LEVEL  0x08        /* Level triggered (edge) mode */
#define ICW1_INIT   0x10        /* Initialization - required! */
 
#define ICW4_8086   0x01        /* 8086/88 (MCS-80/85) mode */
#define ICW4_AUTO   0x02        /* Auto (normal) EOI */
#define ICW4_BUF_SLAVE  0x08        /* Buffered mode/slave */
#define ICW4_BUF_MASTER 0x0C        /* Buffered mode/master */
#define ICW4_SFNM   0x10        /* Special fully nested (not) */
 
#define inb(x,y) asm volatile ("inb %1, %0" : "=a"(x) : "d"(y));

void PIC_remap(BYTE offset1, BYTE offset2)
{
    unsigned char a1, a2, cmd;
    WORD portnum = PIC1_DATA;
    inb(a1, portnum);                        // save masks
    portnum = PIC2_DATA; 
    inb(a2, portnum);
    WORD ret1 = a1, ret2 = a2;
    printf("Response from PIC1 and PIC2: %d %d", ret1, ret2);
    portnum = PIC1_COMMAND;
    cmd = (ICW1_INIT | ICW1_ICW4); 
    outb(portnum, cmd);  // starts the initialization sequence (in cascade mode)
    io_wait();
    portnum = PIC2_COMMAND;
    outb(portnum, cmd);
    io_wait();
    portnum = PIC1_DATA;
    outb(portnum, offset1);                 // ICW2: Master PIC vector offset
    io_wait();
    portnum = PIC2_DATA;
    outb(portnum, offset2);                 // ICW2: Slave PIC vector offset
    io_wait();
    portnum = PIC1_DATA;
    cmd = 4;
    outb(portnum, cmd);                       // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100)
    io_wait();
    portnum = PIC2_DATA;
    cmd = 2;
    outb(portnum, cmd);                       // ICW3: tell Slave PIC its cascade identity (0000 0010)
    io_wait();
    portnum = PIC1_DATA;
    cmd = ICW4_8086;
    outb(portnum, cmd);
    io_wait();
    portnum = PIC2_DATA;
    cmd = ICW4_8086;
    outb(portnum, cmd);
    io_wait();
 
    outb(PIC1_DATA, a1);   // restore saved masks.
    outb(PIC2_DATA, a2);
}

我正在搜索可编程中断控制器在实模式下的行为。但是我遇到了一些问题。预期行为:PIC1 和 PIC2 应返回 0 0。现实:它们返回 184 (0xB8) 和 15 (0xF)。谁能告诉我为什么?

参考:https ://wiki.osdev.org/8259_PIC

标签: cx86x86-16real-modeia16-gcc

解决方案


Zack (The OP) 提供了他们的实现的更新,inb并且这个答案已被修改以提供更具体的答案。inb已通过这种方式定义为

#define inb(x,y) asm volatile ("inb %1, %0" : "=a"(x) : "d"(y));

使用大写标识符(如INB. 从原始代码中我不清楚这是一个宏。宏的替代方法是在共享头文件中创建inb一个static inline函数。您甚至可以标记它,__attribute__((always_inline))以便它在较低的优化级别上内联。该函数可以这样定义:

typedef unsigned short int WORD;
typedef unsigned char BYTE;

#define alwaysinline __attribute__((always_inline))

static inline alwaysinline BYTE inb (WORD portnum)
{
    BYTE byteread;

    asm volatile ("inb %1, %0"
        : "=a"(byteread)
        : "Nd"(portnum));

    return byteread;
}

PIC DATA 端口返回的值

当您从 PIC DATA 端口读取数据时,您将读取用于确定哪些中断会导致 CPU 中断的当前掩码值。它有效地确定在每个 PIC 上启用和禁用哪些特定中断。一个位值0表示启用1表示禁用

如果两个值都为 0,那么 PIC1 和 PIC2 上的所有中断都将被启用。在某些环境中可能是这种情况,但不一定是这种情况。您读到的内容可能不是零,并且表明启用和禁用 BIOS 的中断是什么。

根据截图 PIC1 的值为184(binary 10111000) 而 PIC2 的值为15(binary 00001111)。如果这些实际上是从 PIC 读取的值,则表明 BIOS/固件启用了这些中断(其余的禁用):

IRQ0 - Timer
IRQ1 - Keyboard
IRQ2 - Cascade interrupt
IRQ6 - Usually the Floppy controller

IRQ12 - Mouse/PS2
IRQ13 - Inter Processor interrupt (IPI) - in old days it was for the separate FPU
IRQ14 - Primary ATA Channel (HDD/CD-ROM etc)
IRQ15 - Secondary ATA Channel (HDD/CD-ROM etc)

这些是有道理的,因为它们是您期望在支持旧版 BIOS 和设备的系统上出现的常见中断。尽管您的值不为零,但它们实际上看起来确实合理,并且很可能是从两个 PIC 中读取的真实值


推荐阅读