可编程中断控制器:PIC1 和 PIC2 在实模式下不返回零

Programmable Interrupt Controller: PIC1 and PIC2 are not returning zeros in real mode

超 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 应 return 0 0。 现实:他们 return 184 (0xB8) 和 15 (0xF)。谁能告诉我为什么?

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

Zack(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 数据端口返回的值

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

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

根据屏幕截图,PIC1 的值为 184(二进制 10111000),PIC2 为 15(二进制 00001111)。如果这些实际上是从图片中读取的值,那么它会建议 BIOS/Firmware 启用这些 interrupts(其余禁用):

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 和设备的系统上的常见中断。尽管您的值不为零,但它们实际上看起来很合理,并且很可能是从两张图片中读取的真实值