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