#include <EDK.h>
#include "General.h"
#include "CC64.h"
                
#define Class CC64
extern Class MSVC4Bug;

#undef GetPort

/*
        D000       E000/F000    A000/B000
  ---------------------------------------
  000   RAM        RAM          RAM
  001   Char/RAM   RAM          RAM
  010   Char/RAM   Kernal/RAM   RAM
  011   Char/RAM   Kernal/RAM   Basic/RAM
  100   RAM        RAM          RAM
  101   IO         RAM          RAM
  110   IO         Kernal/RAM   RAM
  111   IO         Kernal/RAM   Basic/RAM
*/


void CC64::SetROMConfig(int iConfig) {
  assert(iConfig >= 0 && iConfig <= 7);
  pCPU->MapReadMem(0x08, apbMapping8000R[iConfig]);
  pCPU->MapReadMem(0x09, apbMapping8000R[iConfig] + 0x1000);
  pCPU->MapReadMem(0x0A, apbMappingA000R[iConfig]);
  pCPU->MapReadMem(0x0B, apbMappingA000R[iConfig] + 0x1000);
  pCPU->MapMem(0x0D, apbMappingD000R[iConfig], apbMappingD000W[iConfig]);
  pCPU->MapReadMem(0x0E, apbMappingE000R[iConfig]);
  pCPU->MapReadMem(0x0F, apbMappingE000R[iConfig] + 0x1000);
  pCPU->AdjustPCPtr();
}


void CC64::InitMemMapping() {

  assert(pbBasic != NULL);
  assert(pbKernal != NULL);
  assert(pbCharset != NULL);
  assert(pbIO != NULL);

  // $8000-$9FFF read
  apbMapping8000R[0] = pbRAM + 0x8000;
  apbMapping8000R[1] = pbRAM + 0x8000;
  apbMapping8000R[2] = pbRAM + 0x8000;
  apbMapping8000R[3] = pbRAM + 0x8000;
  apbMapping8000R[4] = pbRAM + 0x8000;
  apbMapping8000R[5] = pbRAM + 0x8000;
  apbMapping8000R[6] = pbRAM + 0x8000;
  apbMapping8000R[7] = pbRAM + 0x8000;

  // $A000-$BFFF read
  apbMappingA000R[0] = pbRAM + 0xA000;
  apbMappingA000R[1] = pbRAM + 0xA000;
  apbMappingA000R[2] = pbRAM + 0xA000;
  apbMappingA000R[3] = pbBasic;
  apbMappingA000R[4] = pbRAM + 0xA000;
  apbMappingA000R[5] = pbRAM + 0xA000;
  apbMappingA000R[6] = pbRAM + 0xA000;
  apbMappingA000R[7] = pbBasic;

  // $D000-$DFFF read
  apbMappingD000R[0] = pbRAM + 0xD000;
  apbMappingD000R[1] = pbCharset;
  apbMappingD000R[2] = pbCharset;
  apbMappingD000R[3] = pbCharset;
  apbMappingD000R[4] = pbRAM + 0xD000;
  apbMappingD000R[5] = pbIO;
  apbMappingD000R[6] = pbIO;
  apbMappingD000R[7] = pbIO;

  // $D000-$DFFF write
  apbMappingD000W[0] = pbRAM + 0xD000;
  apbMappingD000W[1] = pbRAM + 0xD000;
  apbMappingD000W[2] = pbRAM + 0xD000;
  apbMappingD000W[3] = pbRAM + 0xD000;
  apbMappingD000W[4] = pbRAM + 0xD000;
  apbMappingD000W[5] = pbIO;
  apbMappingD000W[6] = pbIO;
  apbMappingD000W[7] = pbIO;

  // $E000-$FFFF read
  apbMappingE000R[0] = pbRAM + 0xE000;
  apbMappingE000R[1] = pbRAM + 0xE000;
  apbMappingE000R[2] = pbKernal;
  apbMappingE000R[3] = pbKernal;
  apbMappingE000R[4] = pbRAM + 0xE000;
  apbMappingE000R[5] = pbRAM + 0xE000;
  apbMappingE000R[6] = pbKernal;
  apbMappingE000R[7] = pbKernal;

  // map RAM everywhere
  for (int i = 0; i < 16; i++) {
    pCPU->MapMem(i, pbRAM + i * 4096, pbRAM + i * 4096);
  }

  // start with default ROM configuration
  SetROMConfig(7);

}  


// memory configuration in $00/$01 has changed
void CC64::OnMemMappingChange(byte bState, byte bChanges) {
  if ((bChanges & 0x07) != 0) {
    SetROMConfig(bState & 0x07);
  }
}


global void CC64::MapROM8000(byte* pbROM) {
  apbMapping8000R[3] = pbROM;
  apbMapping8000R[7] = pbROM;
  SetROMConfig(MemMapping.GetInput() & 0x07);
}

global void CC64::UnmapROM8000() {
  apbMapping8000R[3] = pbRAM + 0x8000;
  apbMapping8000R[7] = pbRAM + 0x8000;
  SetROMConfig(MemMapping.GetInput() & 0x07);
}

global void CC64::MapROMA000(byte* pbROM) {
  apbMappingA000R[2] = pbROM;
  apbMappingA000R[3] = pbROM;
  apbMappingA000R[6] = pbROM;
  apbMappingA000R[7] = pbROM;
  SetROMConfig(MemMapping.GetInput() & 0x07);
}

global void CC64::UnmapROMA000() {
  apbMappingA000R[2] = pbRAM + 0xA000;
  apbMappingA000R[3] = pbBasic;
  apbMappingA000R[6] = pbRAM + 0xA000;
  apbMappingA000R[7] = pbBasic;
  SetROMConfig(MemMapping.GetInput() & 0x07);
}

