// $Id: I8255.cc 5929 2006-12-15 18:21:09Z manuelbi $ #include "I8255.hh" #include "MSXCliComm.hh" namespace openmsx { const int MODE_A = 0x60; const int MODEA_0 = 0x00; const int MODEA_1 = 0x20; const int MODEA_2 = 0x40; const int MODEA_2_ = 0x60; const int MODE_B = 0x04; const int MODEB_0 = 0x00; const int MODEB_1 = 0x04; const int DIRECTION_A = 0x10; const int DIRECTION_B = 0x02; const int DIRECTION_C0 = 0x01; const int DIRECTION_C1 = 0x08; const int SET_MODE = 0x80; const int BIT_NR = 0x0E; const int SET_RESET = 0x01; I8255::I8255(I8255Interface& interf, const EmuTime& time, MSXCliComm& cliComm_) : interface(interf) , cliComm(cliComm_) , warningPrinted(false) { reset(time); } I8255::~I8255() { } void I8255::reset(const EmuTime& time) { latchPortA = 0; latchPortB = 0; latchPortC = 0; writeControlPort(SET_MODE | DIRECTION_A | DIRECTION_B | DIRECTION_C0 | DIRECTION_C1, time); // all input } byte I8255::readPortA(const EmuTime& time) { switch (control & MODE_A) { case MODEA_0: if (control & DIRECTION_A) { //input return interface.readA(time); // input not latched } else { //output return latchPortA; // output is latched } case MODEA_1: //TODO but not relevant for MSX case MODEA_2: case MODEA_2_: default: return 255; // avoid warning } } byte I8255::peekPortA(const EmuTime& time) const { switch (control & MODE_A) { case MODEA_0: if (control & DIRECTION_A) { return interface.peekA(time); // input not latched } else { return latchPortA; // output is latched } case MODEA_1: //TODO but not relevant for MSX case MODEA_2: case MODEA_2_: default: return 255; } } byte I8255::readPortB(const EmuTime& time) { switch (control & MODE_B) { case MODEB_0: if (control & DIRECTION_B) { //input return interface.readB(time); // input not latched } else { //output return latchPortB; // output is latched } case MODEB_1: // TODO but not relevant for MSX default: return 255; // avoid warning } } byte I8255::peekPortB(const EmuTime& time) const { switch (control & MODE_B) { case MODEB_0: if (control & DIRECTION_B) { return interface.peekB(time); // input not latched } else { return latchPortB; // output is latched } case MODEB_1: // TODO but not relevant for MSX default: return 255; } } byte I8255::readPortC(const EmuTime& time) { byte tmp = readC1(time) | readC0(time); switch (control & MODE_A) { case MODEA_0: // do nothing break; case MODEA_1: case MODEA_2: case MODEA_2_: // TODO but not relevant for MSX break; } switch (control & MODE_B) { case MODEB_0: // do nothing break; case MODEB_1: // TODO but not relevant for MSX break; } return tmp; } byte I8255::peekPortC(const EmuTime& time) const { return peekC1(time) | peekC0(time); } byte I8255::readC1(const EmuTime& time) { if (control & DIRECTION_C1) { //input return interface.readC1(time) << 4; // input not latched } else { //output return latchPortC & 0xf0; // output is latched } } byte I8255::peekC1(const EmuTime& time) const { if (control & DIRECTION_C1) { return interface.peekC1(time) << 4; // input not latched } else { return latchPortC & 0xf0; // output is latched } } byte I8255::readC0(const EmuTime& time) { if (control & DIRECTION_C0) { //input return interface.readC0(time); // input not latched } else { //output return latchPortC & 0x0f; // output is latched } } byte I8255::peekC0(const EmuTime& time) const { if (control & DIRECTION_C0) { return interface.peekC0(time); // input not latched } else { return latchPortC & 0x0f; // output is latched } } byte I8255::readControlPort(const EmuTime& /*time*/) const { return control; } void I8255::writePortA(byte value, const EmuTime& time) { switch (control & MODE_A) { case MODEA_0: // do nothing break; case MODEA_1: case MODEA_2: case MODEA_2_: // TODO but not relevant for MSX break; } outputPortA(value, time); } void I8255::writePortB(byte value, const EmuTime& time) { switch (control & MODE_B) { case MODEB_0: // do nothing break; case MODEB_1: // TODO but not relevant for MSX break; } outputPortB(value, time); } void I8255::writePortC(byte value, const EmuTime& time) { switch (control & MODE_A) { case MODEA_0: // do nothing break; case MODEA_1: case MODEA_2: case MODEA_2_: // TODO but not relevant for MSX break; } switch (control & MODE_B) { case MODEB_0: // do nothing break; case MODEB_1: // TODO but not relevant for MSX break; } outputPortC(value, time); } void I8255::outputPortA(byte value, const EmuTime& time) { latchPortA = value; if (!(control & DIRECTION_A)) { //output interface.writeA(value, time); } } void I8255::outputPortB(byte value, const EmuTime& time) { latchPortB = value; if (!(control & DIRECTION_B)) { //output interface.writeB(value, time); } } void I8255::outputPortC(byte value, const EmuTime& time) { latchPortC = value; if (!(control & DIRECTION_C1)) { //output interface.writeC1(latchPortC >> 4, time); } if (!(control & DIRECTION_C0)) { //output interface.writeC0(latchPortC & 15, time); } } void I8255::writeControlPort(byte value, const EmuTime& time) { if (value & SET_MODE) { // set new control mode control = value; if ((control & (MODE_A | MODE_B)) && !warningPrinted) { warningPrinted = true; cliComm.printWarning("Invalid PPI mode selected. " "This is not yet correctly emulated. " "On a real MSX this will most likely hang."); } outputPortA(latchPortA, time); outputPortB(latchPortB, time); outputPortC(latchPortC, time); } else { // (re)set bit of port C byte bitmask = 1 << ((value & BIT_NR) >> 1); if (value & SET_RESET) { // set latchPortC |= bitmask; } else { // reset latchPortC &= ~bitmask; } outputPortC(latchPortC, time); // check for special (re)set commands // not releant for mode 0 switch (control & MODE_A) { case MODEA_0: // do nothing break; case MODEA_1: case MODEA_2: case MODEA_2_: // TODO but not relevant for MSX break; } switch (control & MODE_B) { case MODEB_0: // do nothing break; case MODEB_1: // TODO but not relevant for MSX break; } } } } // namespace openmsx