i8259.cpp
 1  /* i8259.c - emulation code for the Intel 8259 controller.
 2     Note: This is not a very complete i8259 interrupt controller
 3     implementation, but for the purposes of a PC, it's all we need. */
 4  
 5  #include <stdint.h>
 6  #include <string.h>
 7  
 8  struct structpic {
 9    uint8_t imr; //mask register
10    uint8_t irr; //request register
11    uint8_t isr; //service register
12    uint8_t icwstep; //used during initialization to keep track of which ICW we're at
13    uint8_t icw[5];
14    uint8_t intoffset; //interrupt vector offset
15    uint8_t priority; //which IRQ has highest priority
16    uint8_t autoeoi; //automatic EOI mode
17    uint8_t readmode; //remember what to return on read register from OCW3
18    uint8_t enabled;
19  } i8259;
20  
21  
22  void init8259() {
23       memset((void *)&i8259, 0, sizeof(i8259));
24  }
25  
26  uint8_t in8259(uint16_t portnum) {
27          switch (portnum & 1) {
28                 case 0:
29                      if (i8259.readmode==0) return(i8259.irr); else return(i8259.isr);
30                 case 1: //read mask register
31                      return(i8259.imr);
32                  }
33                  return(0); //can't get here, but the compiler bitches
34  }
35  
36  extern uint32_t makeupticks;
37  void out8259(uint16_t portnum, uint8_t value) {
38       uint8_t i;
39       switch (portnum & 1) {
40              case 0:
41                   if (value & 0x10) { //begin initialization sequence
42                      i8259.icwstep = 1;
43                      i8259.imr = 0; //clear interrupt mask register
44                      i8259.icw[i8259.icwstep++] = value;
45                      return;
46                   }
47                   if ((value & 0x98)==8) { //it's an OCW3
48                      if (value & 2) i8259.readmode = value & 2;
49                   }
50                   if (value & 0x20) { //EOI command
51                      for (i=0; i<8; i++)
52                          if ((i8259.isr >> i) & 1) {
53                             i8259.isr ^= (1 << i);
54                             if ((i==0) && (makeupticks>0)) { makeupticks = 0; i8259.irr |= 1; }
55                             return;
56                          }
57                   }
58                   break;
59              case 1:
60                   if ((i8259.icwstep==3) && (i8259.icw[1] & 2)) i8259.icwstep = 4; //single mode, so don't read ICW3
61                   if (i8259.icwstep<5) { i8259.icw[i8259.icwstep++] = value; return; }
62                   //if we get to this point, this is just a new IMR value
63                   i8259.imr = value;
64                   break;
65       }
66  }
67  
68  uint8_t nextintr() {
69          uint8_t i, tmpirr;
70          tmpirr = i8259.irr & (~i8259.imr); //XOR request register with inverted mask register
71          for (i=0; i<8; i++)
72              if ((tmpirr >> i) & 1) {
73                 i8259.irr ^= (1 << i);
74                 i8259.isr |= (1 << i);
75                 return(i8259.icw[2] + i);
76                          }
77                  return(0); //can't get here, but the compiler bitches
78  }
79  
80  void doirq(uint8_t irqnum) {
81       i8259.irr |= (1 << irqnum);
82  }
83