#include <EDK.h>
#include "General.h"
#include "CVIC656x.h"
#include "CKeyboard.h"

#pragma optimize("g", off)

#define Class CVIC656x
extern Class MSVC4Bug;


proc(ReadVIC18)
  push ESI
  mov ESI,ECX

  mov AL,mvar(bAddress)
  or AL,0x01

  pop ESI
  ret
endp

extern void fn(SetAddress)();
proc(WriteVIC18)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  cmp AL,mvar(bAddress)
  je Same
  mov mvar(bAddress),AL
  call fn(SetAddress)
Same:

  pop ESI
  ret 4
endp

proc(ReadVIC20)
  push ESI
  mov ESI,ECX

  mov AL,byte ptr mvar(i4Frame[0])
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteVIC20)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  or AL,01000000b
  mov byte ptr mvar(i4Frame+0),AL
  mov byte ptr mvar(i4Frame+1),AL
  mov byte ptr mvar(i4Frame+2),AL
  mov byte ptr mvar(i4Frame+3),AL

  pop ESI
  ret 4
endp

proc(ReadVIC21)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abBackECM[0])
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteVIC21)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(abBackECM[0]),AL
  mov byte ptr mvar(iBack),AL
  mov byte ptr mvar(i4Back+0),AL
  mov byte ptr mvar(i4Back+1),AL
  mov byte ptr mvar(i4Back+2),AL
  mov byte ptr mvar(i4Back+3),AL

  pop ESI
  ret 4
endp

proc(ReadVIC22)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abBackECM[1])
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteVIC22)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(abBackECM[1]),AL
  mov byte ptr mvar(iBack2and1+1),AL

  pop ESI
  ret 4
endp

proc(ReadVIC23)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abBackECM[2])
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteVIC23)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(abBackECM[2]),AL
  or AL,0x10
  mov byte ptr mvar(iBack2and1+0),AL

  pop ESI
  ret 4
endp

proc(ReadVIC24)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abBackECM[3])
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteVIC24)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(abBackECM[3]),AL

  pop ESI
  ret 4
endp

proc(ReadVIC11)
  push ESI
  mov ESI,ECX

  mov AL,byte ptr mvar(iLine)+1
  shl AL,7
  or AL,mvar(bControl1)

  pop ESI
  ret
endp

extern void fn(SetBadLine)();
extern void fn(ChangeMode)();

proc(WriteVIC11)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov AH,AL
  shr AH,7
  mov byte ptr mvar(iIntLine+1),AH
  and AL,0x7F
  mov mvar(bControl1),AL
  mov mvar(iScreenOn),51+4
  mov mvar(iScreenOff),251-4
  test AL,8
  je Lines24
  mov mvar(iScreenOn),51
  mov mvar(iScreenOff),251
Lines24:
  and AL,7
  mov mvar(bYScroll),AL
  call fn(SetBadLine)
  mov AL,mvar(bControl1)
  shr AL,2
  xor AL,byte ptr mvar(iMode)
  and EAX,00011000b
  je SameMode
  call fn(ChangeMode)
SameMode:

  pop ESI
  ret 4
endp

proc(ReadVIC12)
  push ESI
  mov ESI,ECX

  mov AL,byte ptr mvar(iLine+0)

  pop ESI
  ret
endp

proc(WriteVIC12)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov byte ptr mvar(iIntLine+0),AL

  pop ESI
  ret 4
endp

proc(ReadVIC16)
  push ESI
  mov ESI,ECX

  mov AL,mvar(bControl2)

  pop ESI
  ret
endp

proc(WriteVIC16)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(bControl2),AL
  and EAX,7
  mov mvar(iXScroll),EAX
  add EAX,mvar(pbLine)
  mov mvar(pbLineShift),EAX
  mov AL,mvar(bControl2)
  add AL,AL
  xor AL,byte ptr mvar(iMode)
  and EAX,00100000b
  je SameMode
  call fn(ChangeMode)
SameMode:

  pop ESI
  ret 4
endp

void CVIC656x::Emulate() {
  __asm {
    mov ThisReg,this
    jmp [ThisReg]Chip.pfnOnClock
  }
}

extern void fn(ResetSprites)();
extern void fn(ResetLP)();
proc(Reset)
  call fn(ResetSprites)
  call fn(ResetLP)
  ret
endp

extern void fn(ReadSprite0X)();
extern void fn(WriteSprite0X)();
extern void fn(ReadSprite0Y)();
extern void fn(WriteSprite0Y)();
extern void fn(ReadSprite1X)();
extern void fn(WriteSprite1X)();
extern void fn(ReadSprite1Y)();
extern void fn(WriteSprite1Y)();
extern void fn(ReadSprite2X)();
extern void fn(WriteSprite2X)();
extern void fn(ReadSprite2Y)();
extern void fn(WriteSprite2Y)();
extern void fn(ReadSprite3X)();
extern void fn(WriteSprite3X)();
extern void fn(ReadSprite3Y)();
extern void fn(WriteSprite3Y)();
extern void fn(ReadSprite4X)();
extern void fn(WriteSprite4X)();
extern void fn(ReadSprite4Y)();
extern void fn(WriteSprite4Y)();
extern void fn(ReadSprite5X)();
extern void fn(WriteSprite5X)();
extern void fn(ReadSprite5Y)();
extern void fn(WriteSprite5Y)();
extern void fn(ReadSprite6X)();
extern void fn(WriteSprite6X)();
extern void fn(ReadSprite6Y)();
extern void fn(WriteSprite6Y)();
extern void fn(ReadSprite7X)();
extern void fn(WriteSprite7X)();
extern void fn(ReadSprite7Y)();
extern void fn(WriteSprite7Y)();
extern void fn(ReadSpriteX8)();
extern void fn(WriteSpriteX8)();

extern void fn(ReadSpriteEnable)();
extern void fn(WriteSpriteEnable)();

extern void fn(ReadLPX)();
extern void fn(ReadLPY)();

extern void fn(ReadSpriteExpandY)();
extern void fn(WriteSpriteExpandY)();

extern void fn(ReadIRR)();
extern void fn(WriteIRR)();
extern void fn(ReadIMR)();
extern void fn(WriteIMR)();

extern void fn(ReadSpriteBack)();
extern void fn(WriteSpriteBack)();
extern void fn(ReadSpriteMC)();
extern void fn(WriteSpriteMC)();
extern void fn(ReadSpriteExpandX)();
extern void fn(WriteSpriteExpandX)();
extern void fn(ReadSSCR)();
extern void fn(WriteSSCR)();
extern void fn(ReadSDCR)();
extern void fn(WriteSDCR)();
extern void fn(ReadSpriteMC1)();
extern void fn(WriteSpriteMC1)();
extern void fn(ReadSpriteMC2)();
extern void fn(WriteSpriteMC2)();
extern void fn(ReadSprite0Color)();
extern void fn(WriteSprite0Color)();
extern void fn(ReadSprite1Color)();
extern void fn(WriteSprite1Color)();
extern void fn(ReadSprite2Color)();
extern void fn(WriteSprite2Color)();
extern void fn(ReadSprite3Color)();
extern void fn(WriteSprite3Color)();
extern void fn(ReadSprite4Color)();
extern void fn(WriteSprite4Color)();
extern void fn(ReadSprite5Color)();
extern void fn(WriteSprite5Color)();
extern void fn(ReadSprite6Color)();
extern void fn(WriteSprite6Color)();
extern void fn(ReadSprite7Color)();
extern void fn(WriteSprite7Color)();

pfnv aVICRegs[64 * 3] = {
  fn(ReadSprite0X), fn(WriteSprite0X), NULL,
  fn(ReadSprite0Y), fn(WriteSprite0Y), NULL,
  fn(ReadSprite1X), fn(WriteSprite1X), NULL,
  fn(ReadSprite1Y), fn(WriteSprite1Y), NULL,
  fn(ReadSprite2X), fn(WriteSprite2X), NULL,
  fn(ReadSprite2Y), fn(WriteSprite2Y), NULL,
  fn(ReadSprite3X), fn(WriteSprite3X), NULL,
  fn(ReadSprite3Y), fn(WriteSprite3Y), NULL,
  fn(ReadSprite4X), fn(WriteSprite4X), NULL,
  fn(ReadSprite4Y), fn(WriteSprite4Y), NULL,
  fn(ReadSprite5X), fn(WriteSprite5X), NULL,
  fn(ReadSprite5Y), fn(WriteSprite5Y), NULL,
  fn(ReadSprite6X), fn(WriteSprite6X), NULL,
  fn(ReadSprite6Y), fn(WriteSprite6Y), NULL,
  fn(ReadSprite7X), fn(WriteSprite7X), NULL,
  fn(ReadSprite7Y), fn(WriteSprite7Y), NULL,
  fn(ReadSpriteX8), fn(WriteSpriteX8), NULL,
  fn(ReadVIC11), fn(WriteVIC11), NULL,
  fn(ReadVIC12), fn(WriteVIC12), NULL,
  fn(ReadLPX),NULL, NULL,
  fn(ReadLPY),NULL, NULL,
  fn(ReadSpriteEnable), fn(WriteSpriteEnable), NULL,
  fn(ReadVIC16), fn(WriteVIC16), NULL,
  fn(ReadSpriteExpandY), fn(WriteSpriteExpandY), NULL,
  fn(ReadVIC18), fn(WriteVIC18), NULL,
  fn(ReadIRR), fn(WriteIRR), NULL,
  fn(ReadIMR), fn(WriteIMR), NULL,
  fn(ReadSpriteBack), fn(WriteSpriteBack), NULL,
  fn(ReadSpriteMC), fn(WriteSpriteMC), NULL,
  fn(ReadSpriteExpandX), fn(WriteSpriteExpandX), NULL,
  fn(ReadSSCR), fn(WriteSSCR), NULL,
  fn(ReadSDCR), fn(WriteSDCR), NULL,
  fn(ReadVIC20), fn(WriteVIC20), NULL,
  fn(ReadVIC21), fn(WriteVIC21), NULL,
  fn(ReadVIC22), fn(WriteVIC22), NULL,
  fn(ReadVIC23), fn(WriteVIC23), NULL,
  fn(ReadVIC24), fn(WriteVIC24), NULL,
  fn(ReadSpriteMC1), fn(WriteSpriteMC1), NULL,
  fn(ReadSpriteMC2), fn(WriteSpriteMC2), NULL,
  fn(ReadSprite0Color), fn(WriteSprite0Color), NULL,
  fn(ReadSprite1Color), fn(WriteSprite1Color), NULL,
  fn(ReadSprite2Color), fn(WriteSprite2Color), NULL,
  fn(ReadSprite3Color), fn(WriteSprite3Color), NULL,
  fn(ReadSprite4Color), fn(WriteSprite4Color), NULL,
  fn(ReadSprite5Color), fn(WriteSprite5Color), NULL,
  fn(ReadSprite6Color), fn(WriteSprite6Color), NULL,
  fn(ReadSprite7Color), fn(WriteSprite7Color), NULL
  // NULL means no register
};


static pfn gpfnFrame1;



void CVIC656x::DoInit() {

  // initialize base class
  // TODO: change Clock to Chip
  Clock::DoInit();
  SetOnClock(gpfnFrame1);
  // SetBusy(); // TODO: change Clock to Chip

  // initialize components
  extern void fn(OnLPLow)();
  LP.Init("LP", this);
  LP.SetOnLow(make_pfn(fn(OnLPLow)));
  Int.Init("Int", this);
  BA.Init("BA", this);
}

int* gpiTimerRoot_Clocks;

CVIC656x::CVIC656x() {

  // 32 byte boundary required for cache optimization
  assert(((int)this & 31) == 0);

  pRegisters = (Register*)aVICRegs;
  iRegisterCount = 64;

  int iChip_pNextChip;
  int iChip_pfnOnClock;
  extern Clock FMS;
  int iClock_TimerRoot_Clocks;
  __asm {
    xor ECX,ECX
    lea EAX,[ECX]Chip.pNextChip
    mov iChip_pNextChip,EAX
    lea EAX,[ECX]Chip.pfnOnClock
    mov iChip_pfnOnClock,EAX
    lea EAX,[ECX]FMS.TimerRoot
    lea EAX,[EAX]Timer.iClocks
    mov iClock_TimerRoot_Clocks,EAX
  }
  if (iChip_pNextChip != 40 || iChip_pfnOnClock != 44 || iClock_TimerRoot_Clocks != 96) {
    trace("Chip_pNextChip equ %d", iChip_pNextChip);
    trace("Chip_pfnOnClock equ %d", iChip_pfnOnClock);
    trace("Clock_TimerRoot_Clocks equ %d", iClock_TimerRoot_Clocks);
    error("Please correct ASM code and CVIC65xx.cpp line 460");
  }
  gpiTimerRoot_Clocks = (int*)((byte*)this + iClock_TimerRoot_Clocks);

  iLine = 0;
  pbBitmap = NULL;
  pbLine = NULL;
  pbLineShift = NULL;
  iVideoLatch = 0;
  iRowCounter = 7;
  iXScroll = 0;
  bYScroll = 0;

  pbGhostByte = NULL;
  
  iMode = 0;

  iIntLine = 511;
  bIRR = 0;
  bIMR = 0;

  i4Back = 0;
  i4Frame = 0;
  iBack = 0;
  iBack2and1 = 0;

  extern void fn(Frame1)();
  __asm {
    mov EAX,offset fnia(Frame1)
    mov gpfnFrame1,EAX
    push ThisReg
    mov ThisReg,this
    call fn(Reset)
    pop ThisReg
  }

}

proc(SetTextMode)
  push EDI
  mov EDI,mvar(pbCharBase)
  xor EAX,EAX
  xor EBX,EBX
  mov AL,mvar(abVideoPtr[0])
  mov BL,mvar(abVideoPtr[1])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[0*4]),ECX
  mov mvar(aiVideoCache[1*4]),EDX
  mov AL,mvar(abVideoPtr[2])
  mov BL,mvar(abVideoPtr[3])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[2*4]),ECX
  mov mvar(aiVideoCache[3*4]),EDX
  mov AL,mvar(abVideoPtr[4])
  mov BL,mvar(abVideoPtr[5])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[4*4]),ECX
  mov mvar(aiVideoCache[5*4]),EDX
  mov AL,mvar(abVideoPtr[6])
  mov BL,mvar(abVideoPtr[7])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[6*4]),ECX
  mov mvar(aiVideoCache[7*4]),EDX
  mov AL,mvar(abVideoPtr[8])
  mov BL,mvar(abVideoPtr[9])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[8*4]),ECX
  mov mvar(aiVideoCache[9*4]),EDX
  mov AL,mvar(abVideoPtr[10])
  mov BL,mvar(abVideoPtr[11])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[10*4]),ECX
  mov mvar(aiVideoCache[11*4]),EDX
  mov AL,mvar(abVideoPtr[12])
  mov BL,mvar(abVideoPtr[13])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[12*4]),ECX
  mov mvar(aiVideoCache[13*4]),EDX
  mov AL,mvar(abVideoPtr[14])
  mov BL,mvar(abVideoPtr[15])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[14*4]),ECX
  mov mvar(aiVideoCache[15*4]),EDX
  mov AL,mvar(abVideoPtr[16])
  mov BL,mvar(abVideoPtr[17])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[16*4]),ECX
  mov mvar(aiVideoCache[17*4]),EDX
  mov AL,mvar(abVideoPtr[18])
  mov BL,mvar(abVideoPtr[19])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[18*4]),ECX
  mov mvar(aiVideoCache[19*4]),EDX
  mov AL,mvar(abVideoPtr[20])
  mov BL,mvar(abVideoPtr[21])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[20*4]),ECX
  mov mvar(aiVideoCache[21*4]),EDX
  mov AL,mvar(abVideoPtr[22])
  mov BL,mvar(abVideoPtr[23])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[22*4]),ECX
  mov mvar(aiVideoCache[23*4]),EDX
  mov AL,mvar(abVideoPtr[24])
  mov BL,mvar(abVideoPtr[25])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[24*4]),ECX
  mov mvar(aiVideoCache[25*4]),EDX
  mov AL,mvar(abVideoPtr[26])
  mov BL,mvar(abVideoPtr[27])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[26*4]),ECX
  mov mvar(aiVideoCache[27*4]),EDX
  mov AL,mvar(abVideoPtr[28])
  mov BL,mvar(abVideoPtr[29])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[28*4]),ECX
  mov mvar(aiVideoCache[29*4]),EDX
  mov AL,mvar(abVideoPtr[30])
  mov BL,mvar(abVideoPtr[31])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[30*4]),ECX
  mov mvar(aiVideoCache[31*4]),EDX
  mov AL,mvar(abVideoPtr[32])
  mov BL,mvar(abVideoPtr[33])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[32*4]),ECX
  mov mvar(aiVideoCache[33*4]),EDX
  mov AL,mvar(abVideoPtr[34])
  mov BL,mvar(abVideoPtr[35])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[34*4]),ECX
  mov mvar(aiVideoCache[35*4]),EDX
  mov AL,mvar(abVideoPtr[36])
  mov BL,mvar(abVideoPtr[37])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[36*4]),ECX
  mov mvar(aiVideoCache[37*4]),EDX
  mov AL,mvar(abVideoPtr[38])
  mov BL,mvar(abVideoPtr[39])
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[38*4]),ECX
  mov mvar(aiVideoCache[39*4]),EDX
  pop EDI
  ret
endp

extern int gaiGraphColors[256];
proc(SetGraphMode)
  xor EAX,EAX
  xor EBX,EBX
  mov AL,mvar(abVideoPtr[0])
  mov BL,mvar(abVideoPtr[1])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[0*4]),ECX
  mov mvar(aiVideoCache[1*4]),EDX
  mov AL,mvar(abVideoPtr[2])
  mov BL,mvar(abVideoPtr[3])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[2*4]),ECX
  mov mvar(aiVideoCache[3*4]),EDX
  mov AL,mvar(abVideoPtr[4])
  mov BL,mvar(abVideoPtr[5])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[4*4]),ECX
  mov mvar(aiVideoCache[5*4]),EDX
  mov AL,mvar(abVideoPtr[6])
  mov BL,mvar(abVideoPtr[7])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[6*4]),ECX
  mov mvar(aiVideoCache[7*4]),EDX
  mov AL,mvar(abVideoPtr[8])
  mov BL,mvar(abVideoPtr[9])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[8*4]),ECX
  mov mvar(aiVideoCache[9*4]),EDX
  mov AL,mvar(abVideoPtr[10])
  mov BL,mvar(abVideoPtr[11])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[10*4]),ECX
  mov mvar(aiVideoCache[11*4]),EDX
  mov AL,mvar(abVideoPtr[12])
  mov BL,mvar(abVideoPtr[13])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[12*4]),ECX
  mov mvar(aiVideoCache[13*4]),EDX
  mov AL,mvar(abVideoPtr[14])
  mov BL,mvar(abVideoPtr[15])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[14*4]),ECX
  mov mvar(aiVideoCache[15*4]),EDX
  mov AL,mvar(abVideoPtr[16])
  mov BL,mvar(abVideoPtr[17])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[16*4]),ECX
  mov mvar(aiVideoCache[17*4]),EDX
  mov AL,mvar(abVideoPtr[18])
  mov BL,mvar(abVideoPtr[19])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[18*4]),ECX
  mov mvar(aiVideoCache[19*4]),EDX
  mov AL,mvar(abVideoPtr[20])
  mov BL,mvar(abVideoPtr[21])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[20*4]),ECX
  mov mvar(aiVideoCache[21*4]),EDX
  mov AL,mvar(abVideoPtr[22])
  mov BL,mvar(abVideoPtr[23])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[22*4]),ECX
  mov mvar(aiVideoCache[23*4]),EDX
  mov AL,mvar(abVideoPtr[24])
  mov BL,mvar(abVideoPtr[25])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[24*4]),ECX
  mov mvar(aiVideoCache[25*4]),EDX
  mov AL,mvar(abVideoPtr[26])
  mov BL,mvar(abVideoPtr[27])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[26*4]),ECX
  mov mvar(aiVideoCache[27*4]),EDX
  mov AL,mvar(abVideoPtr[28])
  mov BL,mvar(abVideoPtr[29])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[28*4]),ECX
  mov mvar(aiVideoCache[29*4]),EDX
  mov AL,mvar(abVideoPtr[30])
  mov BL,mvar(abVideoPtr[31])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[30*4]),ECX
  mov mvar(aiVideoCache[31*4]),EDX
  mov AL,mvar(abVideoPtr[32])
  mov BL,mvar(abVideoPtr[33])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[32*4]),ECX
  mov mvar(aiVideoCache[33*4]),EDX
  mov AL,mvar(abVideoPtr[34])
  mov BL,mvar(abVideoPtr[35])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[34*4]),ECX
  mov mvar(aiVideoCache[35*4]),EDX
  mov AL,mvar(abVideoPtr[36])
  mov BL,mvar(abVideoPtr[37])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[36*4]),ECX
  mov mvar(aiVideoCache[37*4]),EDX
  mov AL,mvar(abVideoPtr[38])
  mov BL,mvar(abVideoPtr[39])
  mov ECX,gaiGraphColors[EAX*4]
  mov EDX,gaiGraphColors[EBX*4]
  mov mvar(aiVideoCache[38*4]),ECX
  mov mvar(aiVideoCache[39*4]),EDX
  ret
endp

proc(SetTextECMode)
  push EDI
  mov EDI,mvar(pbCharBase)
  xor EAX,EAX
  xor EBX,EBX
  mov AL,mvar(abVideoPtr[0])
  mov BL,mvar(abVideoPtr[1])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[0*4]),ECX
  mov mvar(aiVideoCache[1*4]),EDX
  mov AL,mvar(abVideoPtr[2])
  mov BL,mvar(abVideoPtr[3])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[2*4]),ECX
  mov mvar(aiVideoCache[3*4]),EDX
  mov AL,mvar(abVideoPtr[4])
  mov BL,mvar(abVideoPtr[5])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[4*4]),ECX
  mov mvar(aiVideoCache[5*4]),EDX
  mov AL,mvar(abVideoPtr[6])
  mov BL,mvar(abVideoPtr[7])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[6*4]),ECX
  mov mvar(aiVideoCache[7*4]),EDX
  mov AL,mvar(abVideoPtr[8])
  mov BL,mvar(abVideoPtr[9])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[8*4]),ECX
  mov mvar(aiVideoCache[9*4]),EDX
  mov AL,mvar(abVideoPtr[10])
  mov BL,mvar(abVideoPtr[11])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[10*4]),ECX
  mov mvar(aiVideoCache[11*4]),EDX
  mov AL,mvar(abVideoPtr[12])
  mov BL,mvar(abVideoPtr[13])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[12*4]),ECX
  mov mvar(aiVideoCache[13*4]),EDX
  mov AL,mvar(abVideoPtr[14])
  mov BL,mvar(abVideoPtr[15])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[14*4]),ECX
  mov mvar(aiVideoCache[15*4]),EDX
  mov AL,mvar(abVideoPtr[16])
  mov BL,mvar(abVideoPtr[17])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[16*4]),ECX
  mov mvar(aiVideoCache[17*4]),EDX
  mov AL,mvar(abVideoPtr[18])
  mov BL,mvar(abVideoPtr[19])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[18*4]),ECX
  mov mvar(aiVideoCache[19*4]),EDX
  mov AL,mvar(abVideoPtr[20])
  mov BL,mvar(abVideoPtr[21])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[20*4]),ECX
  mov mvar(aiVideoCache[21*4]),EDX
  mov AL,mvar(abVideoPtr[22])
  mov BL,mvar(abVideoPtr[23])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[22*4]),ECX
  mov mvar(aiVideoCache[23*4]),EDX
  mov AL,mvar(abVideoPtr[24])
  mov BL,mvar(abVideoPtr[25])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[24*4]),ECX
  mov mvar(aiVideoCache[25*4]),EDX
  mov AL,mvar(abVideoPtr[26])
  mov BL,mvar(abVideoPtr[27])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[26*4]),ECX
  mov mvar(aiVideoCache[27*4]),EDX
  mov AL,mvar(abVideoPtr[28])
  mov BL,mvar(abVideoPtr[29])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[28*4]),ECX
  mov mvar(aiVideoCache[29*4]),EDX
  mov AL,mvar(abVideoPtr[30])
  mov BL,mvar(abVideoPtr[31])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[30*4]),ECX
  mov mvar(aiVideoCache[31*4]),EDX
  mov AL,mvar(abVideoPtr[32])
  mov BL,mvar(abVideoPtr[33])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[32*4]),ECX
  mov mvar(aiVideoCache[33*4]),EDX
  mov AL,mvar(abVideoPtr[34])
  mov BL,mvar(abVideoPtr[35])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[34*4]),ECX
  mov mvar(aiVideoCache[35*4]),EDX
  mov AL,mvar(abVideoPtr[36])
  mov BL,mvar(abVideoPtr[37])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[36*4]),ECX
  mov mvar(aiVideoCache[37*4]),EDX
  mov AL,mvar(abVideoPtr[38])
  mov BL,mvar(abVideoPtr[39])
  and AL,00111111b
  and BL,00111111b
  lea ECX,[EDI+EAX*8]
  lea EDX,[EDI+EBX*8]
  mov mvar(aiVideoCache[38*4]),ECX
  mov mvar(aiVideoCache[39*4]),EDX
  pop EDI
  ret
endp

proc(SetBlackMode)
  ret
endp

const pfnv gapfnSetMode[8 * 8] = {
  fn(SetTextMode),
  fn(SetGraphMode),
  fn(SetTextECMode),
  fn(SetBlackMode),
  fn(SetTextMode),
  fn(SetGraphMode),
  fn(SetBlackMode),
  fn(SetBlackMode)
};
extern pfnv* gappfnMode[8 * 8];

// EAX = bits to change in iMode
proc(ChangeMode)
  xor EAX,mvar(iMode)
  mov EBX,[ThisReg]Chip.pfnOnClock // 1 down
  mov mvar(iMode),EAX
  GetIndexFromEBX
  sub EBX,15
  jc InFrame
  cmp EBX,41
  jae InFrame
  mov ECX,gappfnMode[EAX*4];
  mov EBX,[ECX+EBX*4]
  mov [ThisReg]Chip.pfnOnClock,EBX
InFrame:
  test AL,fDISPLAY
  je NoMode
  and AL,00111000b
  shr AL,1
  call gapfnSetMode[EAX]
NoMode:
  ret
endp

/*
  pbGhostByte = pbVideoRAM + 0x3FFF;

  int iVideoBase = (bAddress & 0xF0) << 6;
  if (pbCharROM && (iVideoBase & 0x3000) == 0x1000) {
    pbVideoBase = pbCharROM + (iVideoBase & 0x0FFF);
  } else {
    pbVideoBase = pbVideoRAM + iVideoBase;
  }
  pbVideoCounter = pbVideoBase + iVideoLatch;

  int iCharBase = (bAddress & 0x0E) << 10;
  if (pbCharROM && (iCharBase & 0x3000) == 0x0000) {
    pbCharBase = pbCharROM + (iCharBase & 0x0FFF);
  } else {
    pbCharBase = pbVideoRAM + iCharBase;
  }
  pbGraphBase = pbVideoRAM + (iCharBase & 0x2000);
  pbGraphCounter = pbGraphBase + iVideoLatch * 8;
*/

proc(SetAddress)
  mov EAX,mvar(pbVideoRAM)
  add EAX,0x3FFF
  mov mvar(pbGhostByte),EAX

  mov AL,mvar(bAddress)
  and EAX,0xF0
  shl EAX,6
  cmp mvar(pbCharROM),0
  je NormalVideo
  mov BL,AH
  and BL,0x30
  cmp BL,0x10
  jne NormalVideo
  and AH,0x0F
  add EAX,mvar(pbCharROM)
  jmp SetVideo
NormalVideo:
  add EAX,mvar(pbVideoRAM)
SetVideo:
  mov mvar(pbVideoBase),EAX
  add EAX,mvar(iVideoLatch)
  mov mvar(pbVideoCounter),EAX

  mov AL,mvar(bAddress)
  and EAX,0x0E
  shl EAX,10
  mov EBX,EAX
  cmp mvar(pbCharROM),0
  je NormalChar
  mov BL,AH
  and BL,0x30
  cmp BL,0x10
  jne NormalChar
  and AH,0x0F
  add EAX,mvar(pbCharROM)
  jmp SetChar
NormalChar:
  add EAX,mvar(pbVideoRAM)
SetChar:
  mov mvar(pbCharBase),EAX

  and EBX,0x2000
  add EBX,mvar(pbVideoRAM)
  mov EAX,mvar(iVideoLatch)
  mov mvar(pbGraphBase),EBX
  lea EAX,[EBX+EAX*8]
  mov mvar(pbGraphCounter),EAX

  mov EAX,mvar(iMode)
  test AL,fDISPLAY
  je NoMode
  and AL,00111000b
  shr AL,1
  call gapfnSetMode[EAX]
NoMode:
  ret
endp

// EAX = pbVideoRAM
// EBX = pbCharROM or NULL
proc(SetVideoRAMAndCharROM)
  cmp mvar(pbVideoRAM),EAX
  jne Different
  cmp mvar(pbCharROM),EBX
  je Same
Different:
  mov mvar(pbVideoRAM),EAX
  mov mvar(pbCharROM),EBX
  call fn(SetAddress)
Same:
  ret
endp

proc(SetColorRAM)
  mov mvar(pbColorBase),EAX
  add EAX,mvar(iVideoLatch)
  mov mvar(pbColorCounter),EAX
  ret
endp

CVIC656x::~CVIC656x() {
}

#pragma optimize("g", on)

extern Clock* gpClock;
__declspec(naked) void __cdecl ExecEvent() {
  gpClock->Fire();
  __asm jmp [ESI]Chip.pfnOnClock
}
