/*****************************************************************************/
/*                                                                           */
/*   VECTREX HP3000 Software Emulator              Copyright K.Wilkins 1996  */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*   Title      : 6809 Emulator code module                                  */
/*                                                                           */
/*   File Name  : EMU6809.C                                                  */
/*                                                                           */
/*   Author     : Keith Wilkins / C.S.                                       */
/*                                                                           */
/*   Version    : 1.20                                                       */
/*                                                                           */
/*   Desciption : This file contains all of the code that makes up the 6809  */
/*                emulator, it interfaces to the emulator through the        */
/*                functions shown below:                                     */
/*                                                                           */
/*   Functions  : f6809Init           - Initialises the 6809 emulator        */
/*                f6809Reset          - Performs a 6809 hardware reset       */
/*                f6809Tick           - Run a 6809 CPU Cycle                 */
/*                f6809DeclareAddress - Define H/W mapping for emulator      */
/*                f6809AddressRead    - Read from 6809 Address space         */
/*                f6809AddressWrite   - Write to 6809 Address space          */
/*                f6809RegistersRead  - Read 6809 CPU registers (struct)     */
/*                f6809RegistersWrite - Write 6809 CPU regiisters (struct)   */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*   Revision History:                                                       */
/*                                                                           */
/*   Version    Date    Who  Description of changes                          */
/*   -------    ----    ---  ----------------------                          */
/*                                                                           */
/*    0.01    14/03/93  K.W  Creation of empty stub file, description only   */
/*    1.00    05/08/96  K.W  Public release of DVE V1.0                      */
/*    1.20    20/10/96  C.S  MultiTick & Inline Stuff                        */
/*                                                                           */
/*****************************************************************************/

/* Notes : Address read/write functions are not used by the emulator code */
/*         as they write direct, these are for H/W emulation and debug    */
/*         use only. The register read/write passes a pointer to the      */
/*         registers structure defined in 6809emu.h.                      */
/*                                                                        */
/*         Interrupts are handled via the register read/write functions   */
#ifdef _NO_DEBUG_INFORMATION_


#include <stdlib.h>

#include <standard.h>
#include <tools.h>

#include "vdebug.h"
#include "verror.h"
#include "vectrex.h"
#include "emu6809.h"

T_UCHAR atSystemImage[CPU_MEMORY_SIZE];

/*****************************/
/* global <inline> variables */
/*****************************/
T_SWORD  iCount=0;
T_UWORD  iInstruction=0;
T_INT   iBPTriggered=T_FALSE;

/* Define the Local copy of the CPU registers used in all CPU operations */

CPUREGISTERS  tsCPURegisters;

// Define the arrays of read/write function ptrs


T_UCHAR (*apfReadHandlers[CPU_MEMORY_SIZE])(T_UWORD);
T_UCHAR (*apfWriteHandlers[CPU_MEMORY_SIZE])(T_UWORD,T_UCHAR);


/* Define the unmapped memory handlers */

T_UCHAR fUnmappedMemoryRead(T_UWORD tAddr)
{
 fDebug(VERROR,("f6809MemoryRead() - <PC=%04x> Attempt to read from unmapped memory at $%04x",tsCPURegisters.tPC,tAddr));
 tAddr=0;
 return(CPU_UNMAPPED_FILL);
}

T_UCHAR fUnmappedMemoryWrite(T_UWORD tAddr,T_UCHAR tData)
{
 fDebug(VERROR,("f6809MemoryWrite() - <PC=%04x> Attempt to write #$%02x to unmapped memory at $%04x",tsCPURegisters.tPC,tData,tAddr));
 tAddr=0;
 tData=0;
 return(CPU_UNMAPPED_FILL);
}

T_UWORD i6809status=CPU_HALT;
T_UWORD iLastAddress=0;
T_UWORD iBreakpoint=0x0;
T_UWORD iBreakpointEnable=T_FALSE;
T_UWORD iSkipCount=0;

//
// Define a static memory space for "straight" memory applications
//

static T_UCHAR atSystemImage[CPU_MEMORY_SIZE];

//
// Instruction & cycle count file inclusions
//

#include "cod6809.h"
#include "cnt6809.h"

/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809Init()                                               */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : This function initialises the CPU emulator and all assoc- */
/*                 iated arrays i.e the Memory handlers. And then resets the */
/*                 CPU ready for program execution.                          */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : T_UCHAR T_TRUE/T_FALSE depending on success                      */
/*                                                                           */
/*****************************************************************************/

T_INT f6809Init(T_VOID)
{
 T_ULONG loop;
 T_UWORD opcode;
 T_UWORD operand;

 // Initialise the handler array, to unmapped
 for (loop=0;loop<CPU_MEMORY_SIZE;loop++)
 {
  apfReadHandlers[loop]=fUnmappedMemoryRead;
  apfWriteHandlers[loop]=fUnmappedMemoryWrite;
 }

 /* Make sure 6809 is halted */
 i6809status=CPU_HALT;

 // Create and initialise the cycle count lookup table
 // 64K Instruction lookup table - Handles Page 2 & 3 insructions
 // without the need for doing a compare in the 6809tick.

 for (loop=0;loop<0x010000;loop++)
 {
  operand=loop>>8;
  opcode=loop&0xff;
  if (opcode==0x10)
   opcode=0x100+operand;
  if(opcode==0x11)
   opcode=0x200+operand;
  cycle_count[loop]=packed_cycle_count[opcode];
 }
 f6809Reset();
 return(T_TRUE);
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809Reset()                                              */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : This executes a hardware reset of the 6809 CPU. Set all   */
/*                 registers to reset state and fetch the PC from $FFFE and  */
/*                 $FFFF.                                                    */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : nothing                                                   */
/*                                                                           */
/*****************************************************************************/

T_VOID f6809Reset(T_VOID)
{
 T_UWORD    tBootPtr;

 /* On reset DP must be cleared */
 tsCPURegisters.tDP=0;
 /* Clear all other resgisters for the mo */
 tsCPURegisters.tX=0;
 tsCPURegisters.tY=0;
 tsCPURegisters.uABD.tD=0;
 tsCPURegisters.tUSP=0;
 tsCPURegisters.tHSP=0;
 tsCPURegisters.uCC.tCC=0;
 f6809Init8086CC();

//   tsCPURegisters.uCC8086.sCC.bC=1;
//   tsCPURegisters.uCC8086.sCC.bH=1;
//   tsCPURegisters.uCC8086.sCC.bZ=1;
//   tsCPURegisters.uCC8086.sCC.bN=1;
//   tsCPURegisters.uCC8086.sCC.bV=1;

 tsCPURegisters.tPC=0;

 /* Read the PC from $FFFE (high),$FFFF (low) */
 // this looks fairly exotic, but for debug compiling
 // the compiler ALLWAYS leaves garbage in DX, and
 // f6809AddressRead uses DL (NOT DX) for argument passing
 // this exotic thing ensures a clean (non speed optimized) 8 bit value
 // (stupid compiler !!!)
 {
  volatile static T_UCHAR a1;
  volatile static T_UCHAR a2;
  volatile static T_UINT b1;
  volatile static T_UINT b2;
  a1=f6809AddressRead(0xffff);
  a2=f6809AddressRead(0xfffe);
  b1=a1;
  b2=a2;
  b1&=255;
  b2&=255;
  tBootPtr=(T_UWORD) ((b1)+(b2<<8));
 }
 tsCPURegisters.tPC=tBootPtr;
 iLastAddress=tBootPtr;

 /* Clear the Hardware IRQs */
 tsCPURegisters.tNMIFlag=T_FALSE;
 tsCPURegisters.tIRQFlag=T_FALSE;
 tsCPURegisters.tFIRQFlag=T_FALSE;

 /* Mask the IRQ and FIRQ in the CC register */
 tsCPURegisters.uCC.sCC.bI=1;
 tsCPURegisters.uCC.sCC.bF=1;

//   tsCPURegisters.uCC.tCC=tsCPURegisters.tCC | CC_I_FLAG | CC_F_FLAG;
 i6809status=CPU_FETCH;
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809Tick()                                               */
/*                                                                           */
/*   Author      : K.W / C.S.                                                */
/*                                                                           */
/*   Description : This function runs one cycle of CPU time emulation.       */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : nothing                                                   */
/*                                                                           */
/*****************************************************************************/

T_VOID f6809Tick(T_VOID)
{
 // Code moved to sequence.c and then seq.cc and then *cpu.cc
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809LastInsAddr()                                        */
/*                                                                           */
/*   Return address of last/current instruction executed                     */
/*                                                                           */
/*****************************************************************************/

T_UWORD f6809LastInsAddr(T_VOID)
{
 return(iLastAddress);
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809StatusRead()                                         */
/*                                                                           */
/*   Return current status of the processor                                  */
/*                                                                           */
/*****************************************************************************/

T_UWORD f6809StatusRead(T_VOID)
{
 return(i6809status);
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809StatusWrite()                                        */
/*                                                                           */
/*   Set the current status of the processor                                 */
/*                                                                           */
/*****************************************************************************/

T_VOID f6809StatusWrite(T_UWORD tStat)
{
 i6809status=tStat;
 return;
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809SetBreakpoint()                                      */
/*                                                                           */
/*   Set the CPU breakpoint                                                  */
/*                                                                           */
/*****************************************************************************/

T_VOID f6809SetBreakpoint(T_UWORD tBreakpoint,T_UWORD tSkipCount)
{
 iBreakpoint=tBreakpoint;
 iSkipCount=tSkipCount;
 iBreakpointEnable=T_TRUE;
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809SClearBreakpoint()                                   */
/*                                                                           */
/*   Clear processor breakpoint                                              */
/*                                                                           */
/*****************************************************************************/

T_VOID f6809ClearBreakpoint(T_VOID)
{
 iBreakpointEnable=T_FALSE;
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809ReadBreakpoint()                                     */
/*                                                                           */
/*   Readback the processor breakpoint                                       */
/*                                                                           */
/*****************************************************************************/

T_VOID f6809ReadBreakpoint(T_PUWORD tBreakpoint,T_PUWORD tSkip,T_PUWORD tEnable)
{
 if (tBreakpoint!=NULL)
  *tBreakpoint=iBreakpoint;
 if (tSkip!=NULL)
  *tSkip=iSkipCount;
 if (tEnable!=NULL)
  *tEnable=iBreakpointEnable;
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809DelcareAddress()                                     */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : This function declares a memory block to the CPU, so that */
/*                 whenever a read/write is done to that area the read/write */
/*                 functions specified here are called.                      */
/*                                                                           */
/*   Accepts     : T_UWORD address base and block size, function pointers r/w   */
/*                                                                           */
/*   Returns     : T_UCHAR T_TRUE/T_FALSE on success fail.                          */
/*                                                                           */
/*****************************************************************************/

T_INT f6809DeclareAddress(T_UWORD wAddrBase,T_UWORD bSize,T_UCHAR (*pfReadFunc)(T_UWORD),T_UCHAR (*pfWriteFunc)(T_UWORD,T_UCHAR))
{
 T_ULONG lwLoop;
 fDebug(LOW,("f6809DeclareAddress() - Base %04x, Size %04x",wAddrBase,bSize));

 /* No go through the hanlde map array and setup the handle no */
 for (lwLoop=(T_ULONG)wAddrBase;lwLoop<(T_ULONG)wAddrBase+bSize;lwLoop++)
 {
  apfReadHandlers[lwLoop]=pfReadFunc;
  apfWriteHandlers[lwLoop]=pfWriteFunc;
 }

 /* return the handle number, init would have allocated handle=0 */
 /* for the unmapped status so we are guaranteed to be >0==T_TRUE  */
 return(T_TRUE);
}
/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809RegistersRead()                                      */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : This copies the current 6809 register structure into the  */
/*                 given pointer.                                            */
/*                                                                           */
/*   Accepts     : pointer to register structure                             */
/*                                                                           */
/*   Returns     : nothing                                                   */
/*                                                                           */
/*****************************************************************************/

T_VOID f6809RegistersRead(CPUREGISTERS *pts6809Registers)
{
 // Before we release the registers we must update the 6809 CC

 tsCPURegisters.uCC.sCC.bC=tsCPURegisters.uCC8086.sCC.bC;
 tsCPURegisters.uCC.sCC.bH=tsCPURegisters.uCC8086.sCC.bH;
 tsCPURegisters.uCC.sCC.bZ=tsCPURegisters.uCC8086.sCC.bZ;
 tsCPURegisters.uCC.sCC.bN=tsCPURegisters.uCC8086.sCC.bN;
 tsCPURegisters.uCC.sCC.bV=tsCPURegisters.uCC8086.sCC.bV;

 *pts6809Registers=tsCPURegisters;
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809RegistersWrite()                                     */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : This copies the passed resgister structre into the current*/
/*                 CPU register strucure, update ALL registers. When you     */
/*                 want to change a single register do a register read, then */
/*                 modify your chosen register then to a registe write.      */
/*                                                                           */
/*   Accepts     : pointer to a register structure                           */
/*                                                                           */
/*   Returns     : nothing                                                   */
/*                                                                           */
/*****************************************************************************/

T_VOID f6809RegistersWrite(CPUREGISTERS *pts6809Registers)
{
 tsCPURegisters=*pts6809Registers;

 // Before we exit we must update the 8086 CC

 tsCPURegisters.uCC8086.sCC.bC=tsCPURegisters.uCC.sCC.bC;
 tsCPURegisters.uCC8086.sCC.bH=tsCPURegisters.uCC.sCC.bH;
 tsCPURegisters.uCC8086.sCC.bZ=tsCPURegisters.uCC.sCC.bZ;
 tsCPURegisters.uCC8086.sCC.bN=tsCPURegisters.uCC.sCC.bN;
 tsCPURegisters.uCC8086.sCC.bV=tsCPURegisters.uCC.sCC.bV;
}
#endif //#ifdef _NO_DEBUG_INFORMATION_

#ifndef _NO_DEBUG_INFORMATION_

/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809AddressRead()                                        */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Read a T_UCHAR from the given address, using the device      */
/*                 table to revector the read request. The read counters are */
/*                 not incremented by this function.                         */
/*                                                                           */
/*   Accepts     : T_UWORD address                                              */
/*                                                                           */
/*   Returns     : T_UCHAR data read from address                               */
/*                                                                           */
/*****************************************************************************/

T_UCHAR f6809AddressRead(T_UWORD tAddr)
{
 fDebug(LOW,("f6809AddrRead() - Read from %04x",tAddr));
//t_printf(E_VERBOSE_NONE,"\nf6809AddrRead() - Read from %04x",tAddr);
  return( (*apfReadHandlers[tAddr]) (tAddr) );
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : f6809AddressWrite()                                       */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Write a T_UCHAR to the given address, using the device table */
/*                 to revector the write request. The write counters are not */
/*                 incremented by this function.                             */
/*                                                                           */
/*   Accepts     : T_UWORD address and a T_UCHAR of DATA to be written             */
/*                                                                           */
/*   Returns     : T_UCHAR data from write address                              */
/*                                                                           */
/*****************************************************************************/

T_UCHAR f6809AddressWrite(T_UWORD tAddr,T_UCHAR tData)
{
 fDebug(LOW,("f6809AddrWrite() - Write %02x to %04x",tData,tAddr));
 return( (*apfWriteHandlers[tAddr]) (tAddr,tData) );
}

#include "ins6809.h"

#endif //#ifndef _NO_DEBUG_INFORMATION_
