1

Hyper-V output:

enter image description here

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

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Zack Ni
  • 179
  • 3
  • 8
  • Did you ever resolve your problem? – Michael Petch Jul 09 '20 at 03:16
  • @MichaelPetch Yes I did. The reason why it shows these two values is because the port I'm using actually won't return zeros. – Zack Ni Jul 09 '20 at 03:19
  • I think there is more to it than that, but could you do me a favor and add to your question a copy of your `inb` function? – Michael Petch Jul 09 '20 at 03:20
  • @MichaelPetch Yeah sure. I will do that – Zack Ni Jul 09 '20 at 03:21
  • Thanks. Reading those 2 ports likely won't return 0. Reading them should return bits that represent which interrupts are enabled on each of the PICs. They'll be in whatever state the BIOS put the PIC in at boot up. Each bit that 0 is returned corresponds to an interrupt that is enabled (a bit set to 1 is an interrupt that is disabled). – Michael Petch Jul 09 '20 at 03:24
  • I guess they could be zero and all bits enabled. On real hardware they might be something other than 0, but in a VM they are probably all enabled so probably should be 0 – Michael Petch Jul 09 '20 at 03:33
  • @MichaelPetch I am afraid that I have destroyed the inb and outb code. But I do write them the way you did. – Zack Ni Jul 09 '20 at 05:15
  • @MichaelPetch actually I used the macro: #define inb(x,y) asm volatile ("inb %1, %0" : "=a"(x) : "d"(y)); Something like this. – Zack Ni Jul 09 '20 at 05:17

1 Answers1

3

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;
}

Values Returned by PIC DATA Port

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

Michael Petch
  • 46,082
  • 8
  • 107
  • 198