Hyper-V output:
Code:
/*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);
}
I am doing some search on how the programmable interrupt controller behaves under the real mode. But I came up with some problems. Expected behavior: PIC1 and PIC2 should return 0 0. Reality: They return 184 (0xB8) and 15 (0xF). Can any one tell me why?
Reference: https://wiki.osdev.org/8259_PIC
Zack (The OP) provided an update with their implementation of inb
and this answer has been modified to provide a more specific answer. inb
has been defined as a macro this way:
#define inb(x,y) asm volatile ("inb %1, %0" : "=a"(x) : "d"(y));
It might be clearer to name macros with upper case identifiers like INB
. It was unclear to me from the original code that this was a macro at all. An alternative to a macro would be to make inb
a static
inline
function in a shared header file. You could even mark it with __attribute__((always_inline))
so that it gets inlined on lower optimization levels. The function could have been defined this way:
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;
}
When you read from the PIC DATA port you will be reading the current mask values that are used to determine which interrupts will cause the CPU to be interrupted. It effectively determines which specific interrupts are enabled and disabled on each PIC. A bit value of 0
represents enabled and 1
represents disabled.
If both values are 0 then all the interrupts on both PIC1 and PIC2 will be enabled. This may be the case in some environments, but it doesn't necessarily have to be the case. What you read may be non zero and indicative of what interrupts the BIOS enabled and disabled.
According to the screenshot PIC1 has the value 184
(binary 10111000
) and PIC2 is 15
(binary 00001111
). If these are in fact the values read from the PICs then it would suggest the BIOS/Firmware enabled these interrupts (and the rest disabled):
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)
These make sense given they are the common interrupts that you would expect would be present on a system that was supporting legacy BIOS and devices. Although your values are not zero, they do in fact look reasonable and are likely the real values read from both PICs