content: /*
   * HalfNES by Andrew Hoffman
   * Licensed under the GNU GPL Version 3. See LICENSE file
   */
  package com.grapeshot.halfnes.mappers;
  
  import com.grapeshot.halfnes.*;
  
  public class VRC3Mapper extends Mapper {
  
      private int irqctr, irqreload = 0;
      private boolean irqmode, irqenable, irqackenable, interrupted = false;
  
      @Override
      public void loadrom() throws BadMapperException {
          // needs to be in every mapper. Fill with initial cfg
          super.loadrom();
          //swappable bank
          for (int i = 0; i < 16; ++i) {
              prg_map[i] = (1024 * i) & (prgsize - 1);
          }
          //fixed bank
          for (int i = 1; i <= 16; ++i) {
              prg_map[32 - i] = prgsize - (1024 * i);
          }
          for (int i = 0; i < 8; ++i) {
              chr_map[i] = (1024 * i) & (chrsize - 1);
          }
      }
  
      @Override
      public final void cartWrite(int addr, int data) {
          if (addr < 0x8000 || addr > 0xffff) {
              super.cartWrite(addr, data);
              return;
          }
  
          switch (addr >> 12) {
              case 0x8:   //Bits 0-3 of IRQ reload value
                  irqreload = (irqreload & 0xFFF0) | (data & 0xF);
                  break;
              case 0x9:   //Bits 4-7 of IRQ reload value
                  irqreload = (irqreload & 0xFF0F) | (data & 0xF) << 4;
                  break;
              case 0xA:   //Bits 8-11 of IRQ reload value
                  irqreload = (irqreload & 0xF0FF) | (data & 0xF) << 8;
                  break;
              case 0xB:   //Bits 12-15 of IRQ reload value
                  irqreload = (irqreload & 0x0FFF) | (data & 0xF) << 12;
                  break;
              case 0xC:   //IRQ Control
                  irqmode = ((data & (utils.BIT2)) != 0);
                  irqackenable = ((data & (utils.BIT0)) != 0);
  
                  irqenable = ((data & (utils.BIT1)) != 0);
                  if (irqenable) {
                      if (irqmode) {
                          irqctr &= 0xFF00;
                          irqctr |= (irqreload & 0xFF);
                      } else {
                          irqctr = irqreload;
                      }
  
                      if (interrupted) {
                          --cpu.interrupt;
                          interrupted = false;
                      }
                  }
                  break;
              case 0xD:   //IRQ Acknowledge
                  irqenable = irqackenable;
                  if (interrupted) {
                      --cpu.interrupt;
                      interrupted = false;
                  }
                  break;
              case 0xF:   //PRG Select  
                  for (int i = 0; i < 16; ++i) {
                      prg_map[i] = (1024 * (i + 16 * (data & 0xF))) & (prgsize - 1);
                  }
                  break;
          }
      }
  
      @Override
      public void cpucycle(final int cycles) {
          if (irqenable) {
              if (irqmode) {  //8-bit mode
                  int temp = irqctr;
                  irqctr &= 0xFF00;
                  if (temp >= 0xFF) {
                      irqctr = irqreload;
                      irqctr |= (irqreload & 0xFF);
                      if (!interrupted) {
                          ++cpu.interrupt;
                          interrupted = true;
                      }
                  } else {
                      temp += cycles;
                      irqctr |= temp;
                  }
              } else {    //16-bit mode
                  if (irqctr >= 0xFFFF) {
                      irqctr = irqreload;
                      if (!interrupted) {
                          ++cpu.interrupt;
                          interrupted = true;
                      }
                  } else {
                      irqctr += cycles;
                  }
              }
          }
      }
  }
  