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         INTR_A = INTR_B = false;
00169         checkINTR_A();
00170         checkINTR_B();
00171 
00172         /* then reset actual state again */
00173         updateINTR_A();
00174         updateINTR_B();
00175         checkINTR_A();
00176         checkINTR_B();
00177     }
00178     else {
00179         /* bit[7:7] = 0             bit set/reset
00180          * bit[6:4] = X             don't care
00181          * bit[3:1] = bit           bit select
00182          * bit[0:0] = set/reset     1=set 0=reset */
00183         /* single bit set/reset port C */
00184         const uint8_t bit = ((unsigned int)data >> 1U) & 7U;
00185 
00186         if (mode & 0x40) { /* Port A mode 2 */
00187             if (bit == 4) {
00188                 INTE_2 = !!(data & 1);
00189                 updateINTR_A();
00190                 checkINTR_A();
00191             }
00192             else if (bit == 6) {
00193                 INTE_1 = !!(data & 1);
00194                 updateINTR_A();
00195                 checkINTR_A();
00196             }
00197         }
00198         else if (mode & 0x20) { /* Port A mode 1 */
00199             if (bit == ((mode & 0x10) ? /*input*/ 4 : /*output*/6)) {
00200                 INTE_A = !!(data & 1);
00201                 updateINTR_A();
00202                 checkINTR_A();
00203             }
00204         }
00205 
00206         if (mode & 0x04) { /* Port B mode 1 */
00207             if (bit == 2) {
00208                 INTE_B = !!(data & 1);
00209                 updateINTR_B();
00210                 checkINTR_B();
00211             }
00212         }
00213 
00214         writePortC(/*data*/(data & 1U) << bit,/*mask*/1U << bit);
00215     }
00216 }
00217 
00218 uint8_t Intel8255::inPortA(void) const {
00219     return 0x00U; /* override this */
00220 }
00221 
00222 uint8_t Intel8255::inPortB(void) const {
00223     return 0x00U; /* override this */
00224 }
00225 
00226 uint8_t Intel8255::inPortC(void) const {
00227     return 0x00U; /* override this */
00228 }
00229 
00230 void Intel8255::outPortA(const uint8_t mask) {
00231     (void)mask;//UNUSED
00232     /* override this */
00233 }
00234 
00235 void Intel8255::outPortB(const uint8_t mask) {
00236     (void)mask;//UNUSED
00237     /* override this */
00238 }
00239 
00240 void Intel8255::outPortC(const uint8_t mask) {
00241     (void)mask;//UNUSED
00242     /* override this */
00243 }
00244 
00245 void Intel8255::updateINTR_A(void) {
00246     if (mode & 0x40) { /* mode 2 */
00247         INTR_A = (INTE_1 && (!OBF_A)) ^ (INTE_2 && IBF_A); /* OBF goes low when CPU writes, goes high when cleared */
00248     }
00249     else if (mode & 0x20) { /* mode 1 */
00250         if (mode & 0x10)    /* input */
00251             INTR_A = INTE_A && IBF_A;
00252         else                /* output */
00253             INTR_A = INTE_A && (!OBF_A); /* OBF goes low when CPU writes, goes high when cleared */
00254     }
00255     else {
00256         INTR_A = false;
00257     }
00258 }
00259 
00260 void Intel8255::updateINTR_B(void) {
00261     if (mode & 0x04) { /* mode 1 */
00262         if (mode & 0x02)    /* input */
00263             INTR_B = INTE_B && IBF_B;
00264         else                /* output */
00265             INTR_B = INTE_B && (!OBF_B); /* OBF goes low when CPU writes, goes high when cleared */
00266     }
00267     else {
00268         INTR_B = false;
00269     }
00270 }
00271 
00272 void Intel8255::checkINTR_A(void) {
00273     if (pINTR_A != INTR_A) {
00274         pINTR_A  = INTR_A;
00275         sigINTR_A();
00276     }
00277 }
00278 
00279 void Intel8255::checkINTR_B(void) {
00280     if (pINTR_B != INTR_B) {
00281         pINTR_B  = INTR_B;
00282         sigINTR_B();
00283     }
00284 }
00285 
00286 void Intel8255::sigINTR_A(void) {
00287     /* override this */
00288 }
00289 
00290 void Intel8255::sigINTR_B(void) {
00291     /* override this */
00292 }
00293 
00294 void Intel8255::strobePortA(void) {
00295     readPortA(); /* override this */
00296 }
00297 
00298 void Intel8255::strobePortB(void) {
00299     readPortB(); /* override this */
00300 }
00301 
00302 void Intel8255::ackPortA(void) {
00303     OBF_A = false;
00304     updateINTR_A();
00305     checkINTR_A();
00306 }
00307 
00308 void Intel8255::ackPortB(void) {
00309     OBF_B = false;
00310     updateINTR_B();
00311     checkINTR_B();
00312 }
00313 
00314 uint8_t Intel8255::readByPort(uint8_t p03) {
00315     switch (p03) {
00316         case 0: return readPortA();
00317         case 1: return readPortB();
00318         case 2: return readPortC();
00319         case 3: return readControl();
00320     }
00321 
00322     return 0;
00323 }
00324 
00325 void Intel8255::writeByPort(uint8_t p03,uint8_t data) {
00326     switch (p03) {
00327         case 0: writePortA(data);   break;
00328         case 1: writePortB(data);   break;
00329         case 2: writePortC(data);   break;
00330         case 3: writeControl(data); break;
00331     }
00332 }
00333