DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/8255.cpp
00001 
00002 #include "dos_inc.h"
00003 #include "setup.h"
00004 #include "8255.h"
00005 
00006 Intel8255::Intel8255() {
00007     ppiName = NULL;
00008     pINTR_A = pINTR_B = 0;
00009     for (unsigned int c=0;c < 3;c++) {
00010         portNames[c] = NULL;
00011         for (unsigned int i=0;i < 8;i++)
00012             pinNames[c][i] = NULL;
00013     }
00014 }
00015 
00016 Intel8255::~Intel8255() {
00017 }
00018 
00019 void Intel8255::reset(void) {
00020     IBF_A  = IBF_B =  0;
00021     OBF_A  = OBF_B =  0;
00022     INTE_A = INTE_B = 0;
00023     INTE_1 = INTE_2 = 0;
00024     INTR_A = INTR_B = 0;
00025     writeControl(0x9B); /* default: port A input, port B input, port C input mode 0 mode 0 */
00026     latchOutPortA = 0;
00027     latchOutPortB = 0;
00028     latchOutPortC = 0;
00029 }
00030 
00031 uint8_t Intel8255::readPortA(void) {
00032     IBF_A = true;
00033 
00034     latchOutPortA =
00035         (latchOutPortA   &   portAWriteMask ) +
00036         (      inPortA() & (~portAWriteMask));
00037 
00038     updateINTR_A();
00039     IBF_A = false;
00040     checkINTR_A();
00041     return latchOutPortA;
00042 }
00043 
00044 uint8_t Intel8255::readPortB(void) {
00045     IBF_B = true;
00046 
00047     latchOutPortB =
00048         (latchOutPortB   &   portBWriteMask ) +
00049         (      inPortB() & (~portBWriteMask));
00050 
00051     updateINTR_B();
00052     IBF_B = false;
00053     checkINTR_B();
00054     return latchOutPortB;
00055 }
00056 
00057 uint8_t Intel8255::readPortC(void) {
00058     latchOutPortC =
00059         (latchOutPortC   &   portCWriteMask ) +
00060         (      inPortC() & (~portCWriteMask));
00061     return latchOutPortC;
00062 }
00063 
00064 uint8_t Intel8255::readControl(void) {
00065     return mode; /* illegal, but probably reads mode byte */
00066 }
00067 
00068 void Intel8255::writePortA(const uint8_t data,uint8_t mask) {
00069     mask &= portAWriteMask;
00070     latchOutPortA = (latchOutPortA & (~mask)) + (data & mask);
00071     if (mask) {
00072         OBF_A = true;
00073         updateINTR_A();
00074         outPortA(mask);
00075         checkINTR_A();
00076     }
00077 }
00078 
00079 void Intel8255::writePortB(const uint8_t data,uint8_t mask) {
00080     mask &= portBWriteMask;
00081     latchOutPortB = (latchOutPortB & (~mask)) + (data & mask);
00082     if (mask) {
00083         OBF_B = true;
00084         updateINTR_B();
00085         outPortB(mask);
00086         checkINTR_B();
00087     }
00088 }
00089 
00090 void Intel8255::writePortC(const uint8_t data,uint8_t mask) {
00091     mask &= portCWriteMask;
00092     latchOutPortC = (latchOutPortC & (~mask)) + (data & mask);
00093     if (mask) outPortC(mask);
00094 }
00095 
00096 void Intel8255::writeControl(const uint8_t data) {
00097     if (data & 0x80) {
00098         /* bit[7:7] = 1             mode set flag
00099          * bit[6:5] = mode select   00=mode 0  01=mode 1  1x=mode 2
00100          * bit[4:4] = Port A        1=input  0=output
00101          * bit[3:3] = Port C upper  1=input  0=output
00102          * bit[2:2] = mode select   0=mode 0   1=mode 1
00103          * bit[1:1] = Port B        1=input  0=output
00104          * bit[0:0] = Port C lower  1=input  0=output */
00105         /* mode byte */
00106         mode = data;
00107 
00108         /* update write masks */
00109         portAWriteMask  =  (mode & 0x10) ? 0x00 : 0xFF;  /* bit 4 */
00110         portBWriteMask  =  (mode & 0x02) ? 0x00 : 0xFF;  /* bit 1 */
00111         portCWriteMask  = ((mode & 0x01) ? 0x00 : 0x0F) +/* bit 0 */
00112                           ((mode & 0x08) ? 0x00 : 0xF0); /* bit 3 */
00113 
00114         /* modes take additional bits from port C */
00115         if (mode & 0x04) { /* port B mode 1 */
00116             /* port C meanings:
00117              *
00118              * output:
00119              * bit[2:2] = Acknowledge, from device (IN)
00120              * bit[1:1] = Output buffer full aka CPU has written data to this port (OUT)
00121              * bit[0:0] = Interrupt request B (OUT)
00122              *
00123              * input:
00124              * bit[2:2] = Strobe input (loads data into the latch) (IN)
00125              * bit[1:1] = Input buffer full (OUT)
00126              * bit[0:0] = Interrupt request B (OUT) */
00127             portCWriteMask &= ~0x07;
00128         }
00129         if (mode & 0x40) { /* port A mode 2 */
00130             /* port C meanings:
00131              *
00132              * bit[7:7] = Output buffer full aka CPU has written data to this port (OUT)
00133              * bit[6:6] = Acknowledge, from device. This latches the output. Else output is high impedance (IN)
00134              * bit[5:5] = Input buffer full (OUT)
00135              * bit[4:4] = Strobe input (loads data into the latch) (IN)
00136              * bit[3:3] = Interrupt request A (OUT) */
00137             portCWriteMask &= ~0xF8;
00138         }
00139         else if (mode & 0x20) { /* port A mode 1 */
00140             /* port C meanings:
00141              *
00142              * output:
00143              * bit[7:7] = Output buffer full aka CPU has written data to this port (OUT)
00144              * bit[6:6] = Acknowledge, from device (IN)
00145              * bit[3:3] = Interrupt request A (OUT)
00146              *
00147              * input:
00148              * bit[5:5] = Input buffer full (OUT)
00149              * bit[4:4] = Strobe input (loads data input the latch) (IN) 
00150              * bit[3:3] = Interrupt request A (OUT) */
00151             portCWriteMask &= ~((mode & 0x10) ? 0x38 : 0xC8);
00152         }
00153 
00154         /* according to PC-98 hardware it seems changing a port to input makes the latch forget it's contents */
00155         latchOutPortA &= ~portAWriteMask;
00156         latchOutPortB &= ~portBWriteMask;
00157         latchOutPortC &= ~portCWriteMask;
00158 
00159         /* update */
00160         outPortA(portAWriteMask);
00161         outPortB(portBWriteMask);
00162         outPortC(portCWriteMask);
00163 
00164         /* HACK: I get the impression from the PC-98 platform and "Metal Force" that writing the mode
00165          *       byte can cause the chip to re-trigger an interrupt. So... */
00166         /* FIXME: Or am I wrong here, and the retriggering of the interrupt may simply be that internal
00167          *        interrupts on the PC-98 are level triggered? */
00168         /* TODO: It's confirmed PC-98 has edge-triggered interrupts, like IBM PC */
00169         INTR_A = INTR_B = false;
00170         checkINTR_A();
00171         checkINTR_B();
00172 
00173         /* then reset actual state again */
00174         updateINTR_A();
00175         updateINTR_B();
00176         checkINTR_A();
00177         checkINTR_B();
00178     }
00179     else {
00180         /* bit[7:7] = 0             bit set/reset
00181          * bit[6:4] = X             don't care
00182          * bit[3:1] = bit           bit select
00183          * bit[0:0] = set/reset     1=set 0=reset */
00184         /* single bit set/reset port C */
00185         const uint8_t bit = ((unsigned int)data >> 1U) & 7U;
00186 
00187         if (mode & 0x40) { /* Port A mode 2 */
00188             if (bit == 4) {
00189                 INTE_2 = !!(data & 1);
00190                 updateINTR_A();
00191                 checkINTR_A();
00192             }
00193             else if (bit == 6) {
00194                 INTE_1 = !!(data & 1);
00195                 updateINTR_A();
00196                 checkINTR_A();
00197             }
00198         }
00199         else if (mode & 0x20) { /* Port A mode 1 */
00200             if (bit == ((mode & 0x10) ? /*input*/ 4 : /*output*/6)) {
00201                 INTE_A = !!(data & 1);
00202                 updateINTR_A();
00203                 checkINTR_A();
00204             }
00205         }
00206 
00207         if (mode & 0x04) { /* Port B mode 1 */
00208             if (bit == 2) {
00209                 INTE_B = !!(data & 1);
00210                 updateINTR_B();
00211                 checkINTR_B();
00212             }
00213         }
00214 
00215         writePortC(/*data*/(data & 1U) << bit,/*mask*/1U << bit);
00216     }
00217 }
00218 
00219 uint8_t Intel8255::inPortA(void) const {
00220     return 0x00U; /* override this */
00221 }
00222 
00223 uint8_t Intel8255::inPortB(void) const {
00224     return 0x00U; /* override this */
00225 }
00226 
00227 uint8_t Intel8255::inPortC(void) const {
00228     return 0x00U; /* override this */
00229 }
00230 
00231 void Intel8255::outPortA(const uint8_t mask) {
00232     (void)mask;//UNUSED
00233     /* override this */
00234 }
00235 
00236 void Intel8255::outPortB(const uint8_t mask) {
00237     (void)mask;//UNUSED
00238     /* override this */
00239 }
00240 
00241 void Intel8255::outPortC(const uint8_t mask) {
00242     (void)mask;//UNUSED
00243     /* override this */
00244 }
00245 
00246 void Intel8255::updateINTR_A(void) {
00247     if (mode & 0x40) { /* mode 2 */
00248         INTR_A = (INTE_1 && (!OBF_A)) ^ (INTE_2 && IBF_A); /* OBF goes low when CPU writes, goes high when cleared */
00249     }
00250     else if (mode & 0x20) { /* mode 1 */
00251         if (mode & 0x10)    /* input */
00252             INTR_A = INTE_A && IBF_A;
00253         else                /* output */
00254             INTR_A = INTE_A && (!OBF_A); /* OBF goes low when CPU writes, goes high when cleared */
00255     }
00256     else {
00257         INTR_A = false;
00258     }
00259 }
00260 
00261 void Intel8255::updateINTR_B(void) {
00262     if (mode & 0x04) { /* mode 1 */
00263         if (mode & 0x02)    /* input */
00264             INTR_B = INTE_B && IBF_B;
00265         else                /* output */
00266             INTR_B = INTE_B && (!OBF_B); /* OBF goes low when CPU writes, goes high when cleared */
00267     }
00268     else {
00269         INTR_B = false;
00270     }
00271 }
00272 
00273 void Intel8255::checkINTR_A(void) {
00274     if (pINTR_A != INTR_A) {
00275         pINTR_A  = INTR_A;
00276         sigINTR_A();
00277     }
00278 }
00279 
00280 void Intel8255::checkINTR_B(void) {
00281     if (pINTR_B != INTR_B) {
00282         pINTR_B  = INTR_B;
00283         sigINTR_B();
00284     }
00285 }
00286 
00287 void Intel8255::sigINTR_A(void) {
00288     /* override this */
00289 }
00290 
00291 void Intel8255::sigINTR_B(void) {
00292     /* override this */
00293 }
00294 
00295 void Intel8255::strobePortA(void) {
00296     readPortA(); /* override this */
00297 }
00298 
00299 void Intel8255::strobePortB(void) {
00300     readPortB(); /* override this */
00301 }
00302 
00303 void Intel8255::ackPortA(void) {
00304     OBF_A = false;
00305     updateINTR_A();
00306     checkINTR_A();
00307 }
00308 
00309 void Intel8255::ackPortB(void) {
00310     OBF_B = false;
00311     updateINTR_B();
00312     checkINTR_B();
00313 }
00314 
00315 uint8_t Intel8255::readByPort(uint8_t p03) {
00316     switch (p03) {
00317         case 0: return readPortA();
00318         case 1: return readPortB();
00319         case 2: return readPortC();
00320         case 3: return readControl();
00321     }
00322 
00323     return 0;
00324 }
00325 
00326 void Intel8255::writeByPort(uint8_t p03,uint8_t data) {
00327     switch (p03) {
00328         case 0: writePortA(data);   break;
00329         case 1: writePortB(data);   break;
00330         case 2: writePortC(data);   break;
00331         case 3: writeControl(data); break;
00332     }
00333 }
00334