content: package com.grapeshot.halfnes.mappers;
  
  import com.grapeshot.halfnes.*;
  
  public class MMC4Mapper extends Mapper {
  
      boolean chrlatchL = true;
      boolean chrlatchR = false;
      int chrbankL1 = 0;
      int chrbankR1 = 0;
      int chrbankL2 = 0;
      int chrbankR2 = 0;
  
      @Override
      public void loadrom() throws BadMapperException {
          //needs to be in every mapper. Fill with initial cfg
          super.loadrom();
          //on startup:
          for (int i = 1; i <= 32; ++i) {
              prg_map[32 - i] = prgsize - (1024 * i);
          }
          for (int i = 0; i < 8; ++i) {
              chr_map[i] = 0;
          }
      }
  
      @Override
      public final void cartWrite(int addr, int data) {
          if (addr < 0x8000 || addr > 0xffff) {
              super.cartWrite(addr, data);
              return;
          } else if (addr >= 0x8000 && addr <= 0xafff) {
              //remap prg
              for (int i = 0; i < 16; ++i) {
                  prg_map[i] = (1024 * (i + 16 * data)) & (prgsize - 1);
              }
          } else if (addr >= 0xb000 && addr <= 0xbfff) {
              chrbankL1 = data & 0x1f;
              setupPPUBanks();
          } else if (addr >= 0xc000 && addr <= 0xcfff) {
              chrbankL2 = data & 0x1f;
              setupPPUBanks();
          } else if (addr >= 0xd000 && addr <= 0xdfff) {
              chrbankR1 = data & 0x1f;
              setupPPUBanks();
          } else if (addr >= 0xe000 && addr <= 0xefff) {
              chrbankR2 = data & 0x1f;
              setupPPUBanks();
          } else if (addr >= 0xf000 && addr <= 0xffff) {
              setmirroring((((data & (utils.BIT0)) != 0)) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR);
          }
      }
  
      @Override
      public int ppuRead(final int addr) {
          int retval = super.ppuRead(addr);
          if (((addr & (utils.BIT3)) != 0)) {
              //latch fires after 2nd read from pattern table
              //A3 will be on for 2nd read b/c it's tile low bytes
              switch (addr >> 4) {
                  //read of the 2nd byte of tile fd/fe/1fd/1fe
                  //switches the set of character banks used for that side
                  //of the pattern table
                  case 0xfd:
                      chrlatchL = false;
                      setupPPUBanks();
                      break;
                  case 0xfe:
                      chrlatchL = true;
                      setupPPUBanks();
                      break;
                  case 0x1fd:
                      chrlatchR = false;
                      setupPPUBanks();
                      break;
                  case 0x1fe:
                      chrlatchR = true;
                      setupPPUBanks();
                  default:
                      break;
              }
          }
          return retval;
      }
  
      private void setupPPUBanks() {
          if (chrlatchL) {
              setppubank(4, 0, chrbankL2);
          } else {
              setppubank(4, 0, chrbankL1);
          }
          if (chrlatchR) {
              setppubank(4, 4, chrbankR2);
          } else {
              setppubank(4, 4, chrbankR1);
          }
      }
  
      private void setppubank(int banksize, int bankpos, int banknum) {
          for (int i = 0; i < banksize; ++i) {
              chr_map[i + bankpos] = (1024 * ((banksize * banknum) + i)) % chrsize;
          }
      }
  }
  