text: #include "SVIPSG.hh"
  #include "LedStatus.hh"
  #include "MSXCPUInterface.hh"
  #include "MSXMotherBoard.hh"
  #include "JoystickPort.hh"
  #include "serialize.hh"
  #include "stl.hh"
  
  //        Slot 0    Slot 1    Slot 2    Slot 3
  // FFFF +---------+---------+---------+---------+
  //      | Bank 2  | Bank 12 | Bank 22 | Bank 32 | Page 3
  //      |   RAM   |ROM Cart |   RAM   |   RAM   |
  // 8000 |00000000 |01010000 |10100000 |11110000 | Page 2
  //      +---------+---------+---------+---------+
  // 7FFF | Bank 1  | Bank 11 | Bank 21 | Bank 31 | Page 1
  //      |ROM BASIC|ROM Cart |   RAM   |   RAM   |
  //      |00000000 |00000101 |00001010 |00001111 | Page 0
  // 0000 +---------+---------+---------+---------+
  //
  // PSG Port A Input
  // Bit Name   Description
  //  0  FWD1   Joystick 1, Forward
  //  1  BACK1  Joystick 1, Back
  //  2  LEFT1  Joystick 1, Left
  //  3  RIGHT1 Joystick 1, Right
  //  4  FWD2   Joystick 2, Forward
  //  5  BACK2  Joystick 2, Back
  //  6  LEFT2  Joystick 2, Left
  //  7  RIGHT2 Joystick 2, Right
  //
  // PSG Port B Output
  // Bit Name    Description
  // 0   /CART   Memory bank 11, ROM 0000-7FFF (cartridge /CCS1, /CCS2)
  // 1   /BK21   Memory bank 21, RAM 0000-7FFF
  // 2   /BK22   Memory bank 22, RAM 8000-FFFF
  // 3   /BK31   Memory bank 31, RAM 0000-7FFF
  // 4   /BK32   Memory bank 32, RAM 8000-7FFF
  // 5   CAPS    Caps-Lock LED
  // 6   /ROMEN0 Memory bank 12, ROM 8000-BFFF* (cartridge /CCS3)
  // 7   /ROMEN1 Memory bank 12, ROM C000-FFFF* (cartridge /CCS4)
  //
  // * The /CART signal must be active for any effect,
  //   then all banks of RAM are disabled.
  
  namespace openmsx {
  
  // MSXDevice
  
  SVIPSG::SVIPSG(const DeviceConfig& config)
  	: MSXDevice(config)
  	, ports(generate_array<2>([&](auto i) { return &getMotherBoard().getJoystickPort(unsigned(i)); }))
  	, ay8910("PSG", *this, config, getCurrentTime())
  	, prev(255)
  {
  	reset(getCurrentTime());
  }
  
  SVIPSG::~SVIPSG()
  {
  	powerDown(EmuTime::dummy());
  }
  
  void SVIPSG::reset(EmuTime::param time)
  {
  	registerLatch = 0;
  	ay8910.reset(time);
  }
  
  void SVIPSG::powerDown(EmuTime::param /*time*/)
  {
  	getMotherBoard().getLedStatus().setLed(LedStatus::CAPS, false);
  }
  
  byte SVIPSG::readIO(word /*port*/, EmuTime::param time)
  {
  	return ay8910.readRegister(registerLatch, time);
  }
  
  byte SVIPSG::peekIO(word /*port*/, EmuTime::param time) const
  {
  	return ay8910.peekRegister(registerLatch, time);
  }
  
  void SVIPSG::writeIO(word port, byte value, EmuTime::param time)
  {
  	switch (port & 0x07) {
  	case 0:
  		registerLatch = value & 0x0F;
  		break;
  	case 4:
  		ay8910.writeRegister(registerLatch, value, time);
  		break;
  	}
  }
  
  // AY8910Periphery
  
  byte SVIPSG::readA(EmuTime::param time)
  {
  	return ((ports[1]->read(time) & 0x0F) << 4) |
  	       ((ports[0]->read(time) & 0x0F) << 0);
  }
  
  void SVIPSG::writeB(byte value, EmuTime::param /*time*/)
  {
  	getMotherBoard().getLedStatus().setLed(LedStatus::CAPS, (value & 0x20) != 0);
  
  	// Default to bank 1 and 2
  	byte psReg = 0;
  	switch (~value & 0x14) {
  	case 0x04: // bk22
  		psReg = 0xa0;
  		break;
  	case 0x10: // bk32
  		psReg = 0xf0;
  		break;
  	}
  	switch (~value & 0x0B) {
  	case 1: // bk12 (cart)?
  		if ((~value & 0x80) || (~value & 0x40)) {
  			psReg = 0x50;
  		}
  		// bk11 (cart)
  		psReg |= 0x05;
  		break;
  	case 2: // bk21
  		psReg |= 0x0a;
  		break;
  	case 8: // bk31
  		psReg |= 0x0f;
  		break;
  	}
  	getMotherBoard().getCPUInterface().setPrimarySlots(psReg);
  
  	prev = value;
  }
  
  template<typename Archive>
  void SVIPSG::serialize(Archive& ar, unsigned /*version*/)
  {
  	ar.template serializeBase<MSXDevice>(*this);
  	ar.serialize("ay8910",        ay8910,
  	             "registerLatch", registerLatch);
  	byte portB = prev;
  	ar.serialize("portB", portB);
  	if constexpr (Archive::IS_LOADER) {
  		writeB(portB, getCurrentTime());
  	}
  }
  INSTANTIATE_SERIALIZE_METHODS(SVIPSG);
  REGISTER_MSXDEVICE(SVIPSG, "SVI-328 PSG");
  
  } // namespace openmsx
  