////////////////////////////////////////////////////////////////////////////////
// VIC656x.cpp -- this file is part of the Emulator Developers Kit
// available at http://ourworld.compuserve.com/homepages/pc64/develop.htm

#include <EDK.h>

#define global


class VIC656x : public Clock {

  int iCycle;
  byte abVideoPtr[66];
  byte abColorPtr[66];
  int iRasterLine;
  int iRasterIRQ;
  int iXScroll;
  flag fDisplayEnable;
  byte* pbLinePlusXScroll;
  int iVC;
  int iVCLatch;
  int iRC;
  byte* pbVideoBasePlusVC;
  byte* pbGraphBasePlusRC;
  byte abColor2FB1[4];
  pfn* ppfnOnClock;

public:

  // constructor
  global VIC656x() {
  }

  // destructor
  global ~VIC656x() {
  }

  void x();

};


void (VIC656x::*VIC656x_x)() = &VIC656x::x;

void VIC656x::x() {
//  VIC656x_x = &VIC656x::x;
  __asm jmp VIC656x_x
}



void test() {
  __asm jmp VIC656x_x

}





#define TextSCDMA(x) \
  __asm mov EBX,mvar(pbVideoCounter) \
  __asm xor EAX,EAX \
  __asm mov AL,[EBX+x-15] \
  __asm mov mvar(abVideoPtr[x-15]),AL \
  __asm shl EAX,3 \
  __asm add EAX,mvar(pbCharBase) \
  __asm mov EBX,mvar(pbColorCounter) \
  __asm mov mvar(aiVideoCache[(x-15)*4]),EAX \
  __asm mov AL,[EBX+x-15] \
  __asm mov mvar(abColorPtr[x-15]),AL



/* 
single color text

  bData = pbGraphBase[abVideoPtr[i] * 8 + iRowCounter];
  bColor0 = iBack;
  bColor1 = abColorPtr[i]; 
*/




////////////////////////////////////////////////////////////////////////////////
// get address of function if incremental link thunks are used

inline byte* GetTrueAddressOfFunction(void* pfn) {
  byte* pb = (byte*)pfn;
  #ifdef _MSC_VER
    if (*pb == 0xE9) { // jmp
      pb += *(int*)(pb + 1) - 5;
    }
    assert(*pb != 0xE9);
  #endif
  return pb;
}






////////////////////////////////////////////////////////////////////////////////
// build address table

static pfn gapfnOnClock[8][66];
static struct OnLoad {
  OnLoad() {
    extern void VIC656x_Asm();
    byte* pb = GetTrueAddressOfFunction(VIC656x_Asm);
    for (int iMode = 0; iMode < 1/*8*/; iMode++) {
      pfn pfnAddress = NULL;
      assert(*pb == 1);
      for (int iCycle = 1; iCycle <= 63; iCycle++) {
        if (iCycle == *pb) {
          pb += 6;
          pfnAddress = make_pfn(*(void**)(pb - 4));
        }
        gapfnOnClock[iMode][iCycle] = pfnAddress;
      }
    }
  }
} OnLoad;


////////////////////////////////////////////////////////////////////////////////
// VIC656x main implementation in inline assembler with Pentium pairing

#pragma optimize("g", off)
__declspec(naked) void VIC656x_Asm() { __asm {


////////////////////////////////////////////////////////////////////////////////
// table for building gapfnOnClock from start cyle and function address

  _emit 1
  mov EAX,offset XBorder
  _emit 12
  mov EAX,offset CheckDMA
  _emit 13
  mov EAX,offset XBorder
  _emit 16
  mov EAX,offset SingleColorText
  _emit 56
  mov EAX,offset XBorder
  _emit 63
  mov EAX,offset NextLine


////////////////////////////////////////////////////////////////////////////////
// increment system clock and fire timer on zero
// increment VIC cycle and set function for the next clock
// jump to next chip

XBorder:

  // increment system clock and fire timer on zero

  mov EAX,[ESI]Clock.TimerRoot.iClocks
  mov EDI,[ESI]VIC656x.iCycle

  inc EAX
  nop

  mov [ESI]Clock.TimerRoot.iClocks,EAX
  je XBorder_FireTimer

XBorder_FireTimerContinue:

  // increment VIC cycle and set function for the next clock

  inc EDI
  mov EAX,[ESI]VIC656x.ppfnOnClock

  mov [ESI]VIC656x.iCycle,EDI
  nop

  mov EAX,[EAX+EDI*4]
  nop

  mov [ESI]Chip.pfnOnClock,EAX
  nop

  // jump to next chip

  mov EBP,[ESI]Chip.pNextChip
  nop

  mov ESI,EBP
  nop

  ;two
  ;clocks

  ;unpaired
  jmp [EBP]Chip.pfnOnClock

XBorder_FireTimer:

  lea ECX,[ESI]Clock.TimerRoot
  push offset XBorder_FireTimerContinue
  jmp TimerRoot_Fire






////////////////////////////////////////////////////////////////////////////////
// increment system clock and fire timer on zero
// get current character color
// get current graphics data
// output 8 pixels
// increment VIC cycle and set function for the next clock
// jump to next chip

SingleColorText:

  // increment system clock and fire timer on zero

  mov EAX,[ESI]Clock.TimerRoot.iClocks
  mov EDI,[ESI]VIC656x.iCycle

  inc EAX
  nop

  mov [ESI]Clock.TimerRoot.iClocks,EAX
  je SingleColorText_FireTimer

SingleColorText_FireTimerContinue:

  // get current character color

  mov AL,[ESI]VIC656x.abColorPtr[EDI]
  nop

  mov [ESI]VIC656x.abColor2FB1[1],AL
  nop

  // get current graphics data

  xor EAX,EAX
  mov EBX,[ESI]VIC656x.pbGraphBasePlusRC

  mov AL,[ESI]VIC656x.abVideoPtr[EDI]
  nop

  nop
  nop

  mov AL,[EBX+EAX*8]
  nop

  // output 8 pixels

  shl AL,1
  nop

  sbb EBX,EBX
  nop

  shl AL,1
  mov EBP,[ESI]VIC656x.pbLinePlusXScroll

  sbb ECX,ECX
  mov BL,[ESI]VIC656x.abColor2FB1[2+EBX]

  shl AL,1
  mov [EBP+EDI*8+0],BL

  sbb EBX,EBX
  mov CL,[ESI]VIC656x.abColor2FB1[2+ECX]

  shl AL,1
  mov [EBP+EDI*8+1],CL

  sbb ECX,ECX
  mov BL,[ESI]VIC656x.abColor2FB1[2+EBX]

  shl AL,1
  mov [EBP+EDI*8+2],BL

  sbb EBX,EBX
  mov CL,[ESI]VIC656x.abColor2FB1[2+ECX]

  shl AL,1
  mov [EBP+EDI*8+3],CL

  sbb ECX,ECX
  mov BL,[ESI]VIC656x.abColor2FB1[2+EBX]

  shl AL,1
  mov [EBP+EDI*8+4],BL

  sbb EBX,EBX
  mov CL,[ESI]VIC656x.abColor2FB1[2+ECX]

  shl AL,1
  mov [EBP+EDI*8+5],CL

  sbb ECX,ECX
  mov BL,[ESI]VIC656x.abColor2FB1[2+EBX]

  nop
  mov [EBP+EDI*8+6],BL

  nop
  mov CL,[ESI]VIC656x.abColor2FB1[2+ECX]

  nop
  mov [EBP+EDI*8+7],CL

  // increment VIC cycle and set function for the next clock

  inc EDI
  mov EAX,[ESI]VIC656x.ppfnOnClock

  mov [ESI]VIC656x.iCycle,EDI
  nop

  mov EAX,[EAX+EDI*4]
  nop

  mov [ESI]Chip.pfnOnClock,EAX
  nop

  // jump to next chip

  mov EBP,[ESI]Chip.pNextChip
  nop

  mov ESI,EBP
  nop

  ;two
  ;clocks

  ;unpaired
  jmp [EBP]Chip.pfnOnClock

SingleColorText_FireTimer:

  lea ECX,[ESI]Clock.TimerRoot
  push offset SingleColorText_FireTimerContinue
  jmp TimerRoot_Fire

}}
