/*
 * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
 *
 * (c) Copyright 1996 - 2001 Gary Henderson (gary.henderson@ntlworld.com) and
 *                           Jerremy Koot (jkoot@snes9x.com)
 *
 * Super FX C emulator code
 * (c) Copyright 1997 - 1999 Ivar (ivar@snes9x.com) and
 *                           Gary Henderson.
 * Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
 *
 * DSP1 emulator code (c) Copyright 1998 Ivar, _Demo_ and Gary Henderson.
 * C4 asm and some C emulation code (c) Copyright 2000 zsKnight and _Demo_.
 * C4 C code (c) Copyright 2001 Gary Henderson (gary.henderson@ntlworld.com).
 *
 * (c) Copyright 2014 - 2016 Daniel De Matteis. (UNDER NO CIRCUMSTANCE 
 * WILL COMMERCIAL RIGHTS EVER BE APPROPRIATED TO ANY PARTY)
 *
 * DOS port code contains the works of other authors. See headers in
 * individual files.
 *
 * Snes9x homepage: http://www.snes9x.com
 *
 * Permission to use, copy, modify and distribute Snes9x in both binary and
 * source form, for non-commercial purposes, is hereby granted without fee,
 * providing that this license information and copyright notice appear with
 * all copies and any derived work.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event shall the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Snes9x is freeware for PERSONAL USE only. Commercial users should
 * seek permission of the copyright holders first. Commercial use includes
 * charging money for Snes9x or software derived from Snes9x.
 *
 * The copyright holders request that bug fixes and improvements to the code
 * should be forwarded to them so everyone can benefit from the modifications
 * in future versions.
 *
 * Super NES and Super Nintendo Entertainment System are trademarks of
 * Nintendo Co., Limited and its subsidiary companies.
 */

#include "snes9x.h"
#include "memmap.h"
#include "display.h"
#include "cpuexec.h"
#include "apu.h"
#include "spc700.h"

// SPC700/Sound DSP chips have a 24.57MHz crystal on their PCB.

// we only need the memhandlers
#undef INLINE
#define INLINE
#include "apumem.h"

#define OP1 (*(IAPU.PC + 1))
#define OP2 (*(IAPU.PC + 2))

#ifdef SPC700_SHUTDOWN
#define APUShutdown() \
    if (Settings.Shutdown && (IAPU.PC == IAPU.WaitAddress1 || IAPU.PC == IAPU.WaitAddress2)) \
    { \
      if (IAPU.WaitCounter == 0) \
      { \
         if (!ICPU.CPUExecuting) \
            CPU.APU_Cycles = CPU.Cycles = CPU.NextEvent; \
         else \
            CPU.APU_APUExecuting = FALSE; \
      } \
      else \
      if (IAPU.WaitCounter >= 2) \
         IAPU.WaitCounter = 1; \
   else \
       IAPU.WaitCounter--; \
    }
#else
#define APUShutdown()
#endif

#define APUSetZN8(b)\
    IAPU._Zero = (b);

#define APUSetZN16(w)\
    IAPU._Zero = ((w) != 0) | ((w) >> 8);

void STOP(char* s)
{
   char buffer[100];

#ifdef DEBUGGER
   S9xAPUOPrint(buffer, IAPU.PC - IAPU.RAM);
#endif

   sprintf(String, "Sound CPU in unknown state executing %s at %04lX\n%s\n", s, IAPU.PC - IAPU.RAM, buffer);
   S9xMessage(S9X_ERROR, S9X_APU_STOPPED, String);
   APU.TimerEnabled[0] = APU.TimerEnabled[1] = APU.TimerEnabled[2] = FALSE;
   CPU.APU_APUExecuting = FALSE;

#ifdef DEBUGGER
   CPU.Flags |= DEBUG_MODE_FLAG;
#else
   S9xExit();
#endif
}

#define TCALL(n)\
{\
    PushW ((IAPU.PC - IAPU.RAM + 1)); \
    IAPU.PC = IAPU.RAM + (APU.ExtraRAM [((15 - n) << 1)] + \
        (APU.ExtraRAM [((15 - n) << 1) + 1] << 8)); \
}

// XXX: HalfCarry - BJ fixed?
#define SBC(a,b) \
{ \
int16_t Int16 = (int16_t) (a) - (int16_t) (b) + (int16_t) (APUCheckCarry ()) - 1;\
IAPU._Carry = Int16 >= 0;\
if ((((a) ^ (b)) & 0x80) && (((a) ^ (uint8) Int16) & 0x80))\
    APUSetOverflow ();\
else \
    APUClearOverflow (); \
APUSetHalfCarry ();\
if(((a) ^ (b) ^ (uint8) Int16) & 0x10)\
    APUClearHalfCarry ();\
(a) = (uint8) Int16;\
APUSetZN8 ((uint8) Int16); \
}

// XXX: HalfCarry - BJ fixed?
#define ADC(a,b)\
{ \
uint16 Work16 = (a) + (b) + APUCheckCarry(); \
IAPU._Carry = Work16 >= 0x100; \
if (~((a) ^ (b)) & ((b) ^ (uint8) Work16) & 0x80)\
    APUSetOverflow (); \
else \
    APUClearOverflow (); \
APUClearHalfCarry (); \
/*if(((a) ^ (b) ^ (uint8) Int16) & 0x10)  notaz: Int16!? */\
if(((a) ^ (b) ^ (uint8) Work16) & 0x10)\
    APUSetHalfCarry (); \
(a) = (uint8) Work16; \
APUSetZN8 ((uint8) Work16); \
}

#define CMP(a,b)\
{ \
int16_t Int16 = (int16_t) (a) - (int16_t) (b); \
IAPU._Carry = Int16 >= 0; \
APUSetZN8 ((uint8) Int16); \
}

#define ASL(b)\
    IAPU._Carry = ((b) & 0x80) != 0; \
    (b) <<= 1;\
    APUSetZN8 (b);
#define LSR(b)\
    IAPU._Carry = (b) & 1;\
    (b) >>= 1;\
    APUSetZN8 (b);
#define ROL(b)\
{ \
   uint16 Work16 = ((b) << 1) | APUCheckCarry (); \
    IAPU._Carry = Work16 >= 0x100; \
    (b) = (uint8) Work16; \
    APUSetZN8 (b); \
}

#define ROR(b)\
{ \
    uint16 Work16 = (b) | ((uint16) APUCheckCarry () << 8); \
    IAPU._Carry = (uint8) Work16 & 1; \
    Work16 >>= 1; \
    (b) = (uint8) Work16; \
    APUSetZN8 (b); \
}

#define Push(b)\
    *(IAPU.RAM + 0x100 + IAPU.S) = b;\
    IAPU.S--;

#define Pop(b)\
    IAPU.S++;\
    (b) = *(IAPU.RAM + 0x100 + IAPU.S);

#ifdef FAST_LSB_WORD_ACCESS
#define PushW(w)\
    *(uint16 *) (IAPU.RAM + 0xff + IAPU.S) = w;\
    IAPU.S -= 2;
#define PopW(w)\
    IAPU.S += 2;\
    w = *(uint16 *) (IAPU.RAM + 0xff + IAPU.S);
#else
#define PushW(w)\
    *(IAPU.RAM + 0xff + IAPU.S) = w;\
    *(IAPU.RAM + 0x100 + IAPU.S) = (w >> 8);\
    IAPU.S -= 2;
#define PopW(w)\
    IAPU.S += 2; \
    (w) = *(IAPU.RAM + 0xff + IAPU.S) + (*(IAPU.RAM + 0x100 + IAPU.S) << 8);
#endif

#define Relative()\
    int8_t Int8 = OP1;\
    short Int16 = (int) (IAPU.PC + 2 - IAPU.RAM) + Int8;

#define Relative2()\
    int8_t Int8 = OP2;\
    short Int16 = (int) (IAPU.PC + 3 - IAPU.RAM) + Int8;

#ifdef FAST_LSB_WORD_ACCESS
#define IndexedXIndirect()\
    IAPU.Address = *(uint16 *) (IAPU.DirectPage + ((OP1 + IAPU.X) & 0xff));

#define Absolute()\
    IAPU.Address = *(uint16 *) (IAPU.PC + 1);

#define AbsoluteX()\
    IAPU.Address = *(uint16 *) (IAPU.PC + 1) + IAPU.X;

#define AbsoluteY()\
    IAPU.Address = *(uint16 *) (IAPU.PC + 1) + IAPU.YA.B.Y;

#define MemBit()\
    IAPU.Address = *(uint16 *) (IAPU.PC + 1);\
    IAPU.Bit = (uint8)(IAPU.Address >> 13);\
    IAPU.Address &= 0x1fff;

#define IndirectIndexedY()\
    IAPU.Address = *(uint16 *) (IAPU.DirectPage + OP1) + IAPU.YA.B.Y;
#else
#define IndexedXIndirect()\
    IAPU.Address = *(IAPU.DirectPage + ((OP1 + IAPU.X) & 0xff)) + \
        (*(IAPU.DirectPage + ((OP1 + IAPU.X + 1) & 0xff)) << 8);
#define Absolute()\
    IAPU.Address = OP1 + (OP2 << 8);

#define AbsoluteX()\
    IAPU.Address = OP1 + (OP2 << 8) + IAPU.X;

#define AbsoluteY()\
    IAPU.Address = OP1 + (OP2 << 8) + IAPU.YA.B.Y;

#define MemBit()\
    IAPU.Address = OP1 + (OP2 << 8);\
    IAPU.Bit = (int8) (IAPU.Address >> 13);\
    IAPU.Address &= 0x1fff;

#define IndirectIndexedY()\
    IAPU.Address = *(IAPU.DirectPage + OP1) + \
        (*(IAPU.DirectPage + OP1 + 1) << 8) + \
        IAPU.YA.B.Y;
#endif

void Apu00()
{
   // NOP
   IAPU.PC++;
}

void Apu01()
{
   TCALL(0)
}

void Apu11()
{
   TCALL(1)
}

void Apu21()
{
   TCALL(2)
}

void Apu31()
{
   TCALL(3)
}

void Apu41()
{
   TCALL(4)
}

void Apu51()
{
   TCALL(5)
}

void Apu61()
{
   TCALL(6)
}

void Apu71()
{
   TCALL(7)
}

void Apu81()
{
   TCALL(8)
}

void Apu91()
{
   TCALL(9)
}

void ApuA1()
{
   TCALL(10)
}

void ApuB1()
{
   TCALL(11)
}

void ApuC1()
{
   TCALL(12)
}

void ApuD1()
{
   TCALL(13)
}

void ApuE1()
{
   TCALL(14)
}

void ApuF1()
{
   TCALL(15)
}

void Apu3F()  // CALL absolute
{
   Absolute();
   // 0xB6f for Star Fox 2
   PushW((IAPU.PC + 3 - IAPU.RAM));
   IAPU.PC = IAPU.RAM + IAPU.Address;
}

void Apu4F()  // PCALL $XX
{
   uint8 Work8 = OP1;
   PushW((IAPU.PC + 2 - IAPU.RAM));
   IAPU.PC = IAPU.RAM + 0xff00 + Work8;
}

#define SET(b) \
S9xAPUSetByteZ ((uint8) (S9xAPUGetByteZ (OP1 ) | (1 << (b))), OP1); \
IAPU.PC += 2

void Apu02()
{
   SET(0);
}

void Apu22()
{
   SET(1);
}

void Apu42()
{
   SET(2);
}

void Apu62()
{
   SET(3);
}



void Apu82()
{


   SET(4);
}

void ApuA2()
{
   SET(5);
}

void ApuC2()
{
   SET(6);
}

void ApuE2()
{
   SET(7);
}

#define CLR(b) \
S9xAPUSetByteZ ((uint8) (S9xAPUGetByteZ (OP1) & ~(1 << (b))), OP1); \
IAPU.PC += 2;

void Apu12()
{
   CLR(0);
}

void Apu32()
{
   CLR(1);
}

void Apu52()
{
   CLR(2);
}

void Apu72()
{
   CLR(3);
}

void Apu92()
{
   CLR(4);
}

void ApuB2()
{
   CLR(5);
}

void ApuD2()
{
   CLR(6);
}

void ApuF2()
{
   CLR(7);
}

#define BBS(b) \
uint8 Work8 = OP1; \
Relative2 (); \
if (S9xAPUGetByteZ (Work8) & (1 << (b))) \
{ \
    IAPU.PC = IAPU.RAM + (uint16) Int16; \
    CPU.APU_Cycles += IAPU.TwoCycles; \
} \
else \
    IAPU.PC += 3

void Apu03()
{
   BBS(0);
}

void Apu23()
{
   BBS(1);
}

void Apu43()
{
   BBS(2);
}

void Apu63()
{
   BBS(3);
}

void Apu83()
{
   BBS(4);
}

void ApuA3()
{
   BBS(5);
}

void ApuC3()
{
   BBS(6);
}

void ApuE3()
{
   BBS(7);
}

#define BBC(b) \
uint8 Work8 = OP1; \
Relative2 (); \
if (!(S9xAPUGetByteZ (Work8) & (1 << (b)))) \
{ \
    IAPU.PC = IAPU.RAM + (uint16) Int16; \
    CPU.APU_Cycles += IAPU.TwoCycles; \
} \
else \
    IAPU.PC += 3

void Apu13()
{
   BBC(0);
}

void Apu33()
{
   BBC(1);
}

void Apu53()
{
   BBC(2);
}

void Apu73()
{
   BBC(3);
}

void Apu93()
{
   BBC(4);
}

void ApuB3()
{
   BBC(5);
}

void ApuD3()
{
   BBC(6);
}

void ApuF3()
{
   BBC(7);
}

void Apu04()
{
   // OR A,dp
   IAPU.YA.B.A |= S9xAPUGetByteZ(OP1);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu05()
{
   // OR A,abs
   Absolute();
   IAPU.YA.B.A |= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 3;
}

void Apu06()
{
   // OR A,(X)
   IAPU.YA.B.A |= S9xAPUGetByteZ(IAPU.X);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC++;
}

void Apu07()
{
   // OR A,(dp+X)
   IndexedXIndirect();
   IAPU.YA.B.A |= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu08()
{
   // OR A,#00
   IAPU.YA.B.A |= OP1;
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu09()
{
   // OR dp(dest),dp(src)
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   Work8 |= S9xAPUGetByteZ(OP2);
   S9xAPUSetByteZ(Work8, OP2);
   APUSetZN8(Work8);
   IAPU.PC += 3;
}

void Apu14()
{
   // OR A,dp+X
   IAPU.YA.B.A |= S9xAPUGetByteZ(OP1 + IAPU.X);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu15()
{
   // OR A,abs+X
   AbsoluteX();
   IAPU.YA.B.A |= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 3;
}

void Apu16()
{
   // OR A,abs+Y
   AbsoluteY();
   IAPU.YA.B.A |= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 3;
}

void Apu17()
{
   // OR A,(dp)+Y
   IndirectIndexedY();
   IAPU.YA.B.A |= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu18()
{
   // OR dp,#00
   uint8 Work8 = OP1;
   Work8 |= S9xAPUGetByteZ(OP2);
   S9xAPUSetByteZ(Work8, OP2);
   APUSetZN8(Work8);
   IAPU.PC += 3;
}

void Apu19()
{
   // OR (X),(Y)
   uint8 Work8 = S9xAPUGetByteZ(IAPU.X) | S9xAPUGetByteZ(IAPU.YA.B.Y);
   APUSetZN8(Work8);
   S9xAPUSetByteZ(Work8, IAPU.X);
   IAPU.PC++;
}

void Apu0A()
{
   // OR1 C,membit
   MemBit();
   if (!APUCheckCarry())
   {
      if (S9xAPUGetByte(IAPU.Address) & (1 << IAPU.Bit))
         APUSetCarry();
   }
   IAPU.PC += 3;
}

void Apu2A()
{
   // OR1 C,not membit
   MemBit();
   if (!APUCheckCarry())
   {
      if (!(S9xAPUGetByte(IAPU.Address) & (1 << IAPU.Bit)))
         APUSetCarry();
   }
   IAPU.PC += 3;
}

void Apu4A()
{
   // AND1 C,membit
   MemBit();
   if (APUCheckCarry())
   {
      if (!(S9xAPUGetByte(IAPU.Address) & (1 << IAPU.Bit)))
         APUClearCarry();
   }
   IAPU.PC += 3;
}

void Apu6A()
{
   // AND1 C, not membit
   MemBit();
   if (APUCheckCarry())
   {
      if ((S9xAPUGetByte(IAPU.Address) & (1 << IAPU.Bit)))
         APUClearCarry();
   }
   IAPU.PC += 3;
}

void Apu8A()
{
   // EOR1 C, membit
   MemBit();
   if (APUCheckCarry())
   {
      if (S9xAPUGetByte(IAPU.Address) & (1 << IAPU.Bit))
         APUClearCarry();
   }
   else
   {
      if (S9xAPUGetByte(IAPU.Address) & (1 << IAPU.Bit))
         APUSetCarry();
   }
   IAPU.PC += 3;
}

void ApuAA()
{
   // MOV1 C,membit
   MemBit();
   if (S9xAPUGetByte(IAPU.Address) & (1 << IAPU.Bit))
      APUSetCarry();
   else
      APUClearCarry();
   IAPU.PC += 3;
}

void ApuCA()
{
   // MOV1 membit,C
   MemBit();
   if (APUCheckCarry())
      S9xAPUSetByte(S9xAPUGetByte(IAPU.Address) | (1 << IAPU.Bit), IAPU.Address);
   else
      S9xAPUSetByte(S9xAPUGetByte(IAPU.Address) & ~(1 << IAPU.Bit), IAPU.Address);
   IAPU.PC += 3;
}

void ApuEA()
{
   // NOT1 membit
   MemBit();
   S9xAPUSetByte(S9xAPUGetByte(IAPU.Address) ^ (1 << IAPU.Bit), IAPU.Address);
   IAPU.PC += 3;
}

void Apu0B()
{
   // ASL dp
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   ASL(Work8);
   S9xAPUSetByteZ(Work8, OP1);
   IAPU.PC += 2;
}

void Apu0C(void)
{
   uint8 Work8;
   // ASL abs
   Absolute();
   Work8 = S9xAPUGetByte(IAPU.Address);
   ASL(Work8);
   S9xAPUSetByte(Work8, IAPU.Address);
   IAPU.PC += 3;
}

void Apu1B(void)
{
   // ASL dp+X
   uint8 Work8 = S9xAPUGetByteZ(OP1 + IAPU.X);
   ASL(Work8);
   S9xAPUSetByteZ(Work8, OP1 + IAPU.X);
   IAPU.PC += 2;
}

void Apu1C()
{
   // ASL A
   ASL(IAPU.YA.B.A);
   IAPU.PC++;
}

void Apu0D()
{
   // PUSH PSW
   S9xAPUPackStatus();
   Push(IAPU.P);
   IAPU.PC++;
}

void Apu2D()
{
   // PUSH A
   Push(IAPU.YA.B.A);
   IAPU.PC++;
}

void Apu4D()
{
   // PUSH X
   Push(IAPU.X);
   IAPU.PC++;
}

void Apu6D()
{
   // PUSH Y
   Push(IAPU.YA.B.Y);
   IAPU.PC++;
}

void Apu8E()
{
   // POP PSW
   Pop(IAPU.P);
   S9xAPUUnpackStatus();
   if (APUCheckDirectPage())
      IAPU.DirectPage = IAPU.RAM + 0x100;
   else
      IAPU.DirectPage = IAPU.RAM;
   IAPU.PC++;
}

void ApuAE()
{
   // POP A
   Pop(IAPU.YA.B.A);
   IAPU.PC++;
}

void ApuCE()
{
   // POP X
   Pop(IAPU.X);
   IAPU.PC++;
}

void ApuEE()
{
   // POP Y
   Pop(IAPU.YA.B.Y);
   IAPU.PC++;
}

void Apu0E()
{
   uint8 Work8;
   // TSET1 abs
   Absolute();
   Work8 = S9xAPUGetByte(IAPU.Address);
   S9xAPUSetByte(Work8 | IAPU.YA.B.A, IAPU.Address);
   Work8 &= IAPU.YA.B.A;
   APUSetZN8(Work8);
   IAPU.PC += 3;
}

void Apu4E()
{
   uint8 Work8;
   // TCLR1 abs
   Absolute();
   Work8 = S9xAPUGetByte(IAPU.Address);
   S9xAPUSetByte(Work8 & ~IAPU.YA.B.A, IAPU.Address);
   Work8 &= IAPU.YA.B.A;
   APUSetZN8(Work8);
   IAPU.PC += 3;
}

void Apu0F()
{
   // BRK

#if 0
   STOP("BRK");
#else
   PushW((IAPU.PC + 1 - IAPU.RAM));
   S9xAPUPackStatus();
   Push(IAPU.P);
   APUSetBreak();
   APUClearInterrupt();
   // XXX:Where is the BRK vector ???
   IAPU.PC = IAPU.RAM + APU.ExtraRAM[0x20] + (APU.ExtraRAM[0x21] << 8);
#endif
}

void ApuEF()
{
   // SLEEP
   // XXX: sleep
   // STOP ("SLEEP");
   CPU.APU_APUExecuting = FALSE;
   IAPU.PC++;
}

void ApuFF()
{
   // STOP
   // STOP ("STOP");
   CPU.APU_APUExecuting = FALSE;
   IAPU.PC++;
}

void Apu10()
{
   // BPL
   Relative();
   if (!APUCheckNegative())
   {
      IAPU.PC = IAPU.RAM + (uint16) Int16;
      CPU.APU_Cycles += IAPU.TwoCycles;
      APUShutdown();
   }
   else
      IAPU.PC += 2;
}

void Apu30()
{
   // BMI
   Relative();
   if (APUCheckNegative())
   {
      IAPU.PC = IAPU.RAM + (uint16) Int16;
      CPU.APU_Cycles += IAPU.TwoCycles;
      APUShutdown();
   }
   else
      IAPU.PC += 2;
}

void Apu90()
{
   // BCC
   Relative();
   if (!APUCheckCarry())
   {
      IAPU.PC = IAPU.RAM + (uint16) Int16;
      CPU.APU_Cycles += IAPU.TwoCycles;
      APUShutdown();
   }
   else
      IAPU.PC += 2;
}

void ApuB0()
{
   // BCS
   Relative();
   if (APUCheckCarry())
   {

      IAPU.PC = IAPU.RAM + (uint16) Int16;
      CPU.APU_Cycles += IAPU.TwoCycles;
      APUShutdown();
   }
   else
      IAPU.PC += 2;
}

void ApuD0()
{
   // BNE
   Relative();
   if (!APUCheckZero())
   {
      IAPU.PC = IAPU.RAM + (uint16) Int16;
      CPU.APU_Cycles += IAPU.TwoCycles;
      APUShutdown();
   }
   else
      IAPU.PC += 2;
}

void ApuF0()
{
   // BEQ
   Relative();
   if (APUCheckZero())
   {
      IAPU.PC = IAPU.RAM + (uint16) Int16;
      CPU.APU_Cycles += IAPU.TwoCycles;
      APUShutdown();
   }
   else
      IAPU.PC += 2;
}

void Apu50()
{
   // BVC
   Relative();
   if (!APUCheckOverflow())
   {
      IAPU.PC = IAPU.RAM + (uint16) Int16;
      CPU.APU_Cycles += IAPU.TwoCycles;
   }
   else
      IAPU.PC += 2;
}

void Apu70()
{
   // BVS
   Relative();
   if (APUCheckOverflow())
   {
      IAPU.PC = IAPU.RAM + (uint16) Int16;
      CPU.APU_Cycles += IAPU.TwoCycles;
   }
   else
      IAPU.PC += 2;
}

void Apu2F()
{
   // BRA
   Relative();
   IAPU.PC = IAPU.RAM + (uint16) Int16;
}

void Apu80()
{
   // SETC
   APUSetCarry();
   IAPU.PC++;
}

void ApuED()
{
   // NOTC
   IAPU._Carry ^= 1;
   IAPU.PC++;
}

void Apu40()
{
   // SETP
   APUSetDirectPage();
   IAPU.DirectPage = IAPU.RAM + 0x100;
   IAPU.PC++;
}

void Apu1A()
{
   // DECW dp
   uint16 Work16 = S9xAPUGetByteZ(OP1) + (S9xAPUGetByteZ(OP1 + 1) << 8);
   Work16--;
   S9xAPUSetByteZ((uint8) Work16, OP1);
   S9xAPUSetByteZ(Work16 >> 8, OP1 + 1);
   APUSetZN16(Work16);
   IAPU.PC += 2;
}

void Apu5A()
{
   // CMPW YA,dp
   uint16 Work16 = S9xAPUGetByteZ(OP1) + (S9xAPUGetByteZ(OP1 + 1) << 8);
   long Int32 = (long) IAPU.YA.W - (long) Work16;
   IAPU._Carry = Int32 >= 0;
   APUSetZN16((uint16) Int32);
   IAPU.PC += 2;
}

void Apu3A()
{
   // INCW dp
   uint16 Work16 = S9xAPUGetByteZ(OP1) + (S9xAPUGetByteZ(OP1 + 1) << 8);
   Work16++;
   S9xAPUSetByteZ((uint8) Work16, OP1);
   S9xAPUSetByteZ(Work16 >> 8, OP1 + 1);
   APUSetZN16(Work16);
   IAPU.PC += 2;
}

// XXX: HalfCarry - BJ Fixed? Or is it between bits 7 and 8 for ADDW/SUBW?
void Apu7A()
{
   // ADDW YA,dp
   uint16 Work16 = S9xAPUGetByteZ(OP1) + (S9xAPUGetByteZ(OP1 + 1) << 8);
   uint32 Work32 = (uint32) IAPU.YA.W + Work16;
   IAPU._Carry = Work32 >= 0x10000;
   if (~(IAPU.YA.W ^ Work16) & (Work16 ^ (uint16) Work32) & 0x8000)
      APUSetOverflow();
   else
      APUClearOverflow();
   APUClearHalfCarry();
   if ((IAPU.YA.W ^ Work16 ^ (uint16) Work32) & 0x10)
      APUSetHalfCarry();
   IAPU.YA.W = (uint16) Work32;
   APUSetZN16(IAPU.YA.W);
   IAPU.PC += 2;
}

// XXX: BJ: i think the old HalfCarry behavior was wrong...
// XXX: Or is it between bits 7 and 8 for ADDW/SUBW?
void Apu9A()
{
   // SUBW YA,dp
   uint16 Work16 = S9xAPUGetByteZ(OP1) + (S9xAPUGetByteZ(OP1 + 1) << 8);
   long Int32 = (long) IAPU.YA.W - (long) Work16;
   APUClearHalfCarry();
   IAPU._Carry = Int32 >= 0;
   if (((IAPU.YA.W ^ Work16) & 0x8000) &&
         ((IAPU.YA.W ^ (uint16) Int32) & 0x8000))
      APUSetOverflow();
   else
      APUClearOverflow();
   //    if (((IAPU.YA.W ^ Work16) & 0x0080) &&
   //     ((IAPU.YA.W ^ (uint16) Int32) & 0x0080))
   // APUSetHalfCarry ();                              // notaz: strange here
   APUSetHalfCarry();
   //    if((IAPU.YA.W ^ Work16 ^ (uint16) Work32) & 0x10) // notaz: Work32?!
   if ((IAPU.YA.W ^ Work16 ^ (uint16) Int32) & 0x10)
      APUClearHalfCarry();
   IAPU.YA.W = (uint16) Int32;
   APUSetZN16(IAPU.YA.W);
   IAPU.PC += 2;
}

void ApuBA()
{
   // MOVW YA,dp
   IAPU.YA.B.A = S9xAPUGetByteZ(OP1);
   IAPU.YA.B.Y = S9xAPUGetByteZ(OP1 + 1);
   APUSetZN16(IAPU.YA.W);
   IAPU.PC += 2;
}

void ApuDA()
{
   // MOVW dp,YA
   S9xAPUSetByteZ(IAPU.YA.B.A, OP1);
   S9xAPUSetByteZ(IAPU.YA.B.Y, OP1 + 1);
   IAPU.PC += 2;
}

void Apu64()
{
   // CMP A,dp
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   CMP(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void Apu65()
{
   uint8 Work8;
   // CMP A,abs
   Absolute();
   Work8 = S9xAPUGetByte(IAPU.Address);
   CMP(IAPU.YA.B.A, Work8);
   IAPU.PC += 3;
}

void Apu66()
{
   // CMP A,(X)
   uint8 Work8 = S9xAPUGetByteZ(IAPU.X);
   CMP(IAPU.YA.B.A, Work8);
   IAPU.PC++;
}

void Apu67()
{
   uint8 Work8;
   // CMP A,(dp+X)
   IndexedXIndirect();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   CMP(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void Apu68()
{
   // CMP A,#00
   uint8 Work8 = OP1;
   CMP(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void Apu69()
{
   // CMP dp(dest), dp(src)
   uint8 W1 = S9xAPUGetByteZ(OP1);
   uint8 Work8 = S9xAPUGetByteZ(OP2);
   CMP(Work8, W1);
   IAPU.PC += 3;
}

void Apu74()
{
   // CMP A, dp+X
   uint8 Work8 = S9xAPUGetByteZ(OP1 + IAPU.X);
   CMP(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void Apu75()
{
   uint8 Work8;
   // CMP A,abs+X
   AbsoluteX();
   Work8 = S9xAPUGetByte(IAPU.Address);
   CMP(IAPU.YA.B.A, Work8);
   IAPU.PC += 3;
}

void Apu76()
{
   uint8 Work8;
   // CMP A, abs+Y
   AbsoluteY();
   Work8 = S9xAPUGetByte(IAPU.Address);
   CMP(IAPU.YA.B.A, Work8);
   IAPU.PC += 3;
}

void Apu77()
{
   uint8 Work8;
   // CMP A,(dp)+Y
   IndirectIndexedY();
   Work8 = S9xAPUGetByte(IAPU.Address);
   CMP(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void Apu78()
{
   // CMP dp,#00
   uint8 Work8 = OP1;
   uint8 W1 = S9xAPUGetByteZ(OP2);
   CMP(W1, Work8);
   IAPU.PC += 3;
}

void Apu79()
{
   // CMP (X),(Y)
   uint8 W1 = S9xAPUGetByteZ(IAPU.X);
   uint8 Work8 = S9xAPUGetByteZ(IAPU.YA.B.Y);
   CMP(W1, Work8);
   IAPU.PC++;
}

void Apu1E()
{
   uint8 Work8;
   // CMP X,abs
   Absolute();
   Work8 = S9xAPUGetByte(IAPU.Address);
   CMP(IAPU.X, Work8);
   IAPU.PC += 3;
}

void Apu3E()
{
   // CMP X,dp
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   CMP(IAPU.X, Work8);
   IAPU.PC += 2;
}

void ApuC8()
{
   // CMP X,#00
   CMP(IAPU.X, OP1);
   IAPU.PC += 2;
}

void Apu5E()
{
   uint8 Work8;
   // CMP Y,abs
   Absolute();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   CMP(IAPU.YA.B.Y, Work8);
   IAPU.PC += 3;
}

void Apu7E()
{
   // CMP Y,dp
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   CMP(IAPU.YA.B.Y, Work8);
   IAPU.PC += 2;
}

void ApuAD()
{
   // CMP Y,#00
   uint8 Work8 = OP1;
   CMP(IAPU.YA.B.Y, Work8);
   IAPU.PC += 2;
}

void Apu1F()
{
   // JMP (abs+X)
   Absolute();
   IAPU.PC = IAPU.RAM + S9xAPUGetByte(IAPU.Address + IAPU.X) +
             (S9xAPUGetByte(IAPU.Address + IAPU.X + 1) << 8);
   // XXX: HERE:
   // APU.Flags |= TRACE_FLAG;
}

void Apu5F()
{
   // JMP abs
   Absolute();
   IAPU.PC = IAPU.RAM + IAPU.Address;
}

void Apu20()
{
   // CLRP
   APUClearDirectPage();
   IAPU.DirectPage = IAPU.RAM;
   IAPU.PC++;
}

void Apu60()
{
   // CLRC
   APUClearCarry();
   IAPU.PC++;
}

void ApuE0()
{
   // CLRV
   APUClearHalfCarry();
   APUClearOverflow();
   IAPU.PC++;
}

void Apu24()
{
   // AND A,dp
   IAPU.YA.B.A &= S9xAPUGetByteZ(OP1);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu25()
{
   // AND A,abs
   Absolute();
   IAPU.YA.B.A &= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 3;
}

void Apu26()
{
   // AND A,(X)
   IAPU.YA.B.A &= S9xAPUGetByteZ(IAPU.X);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC++;
}

void Apu27()
{
   // AND A,(dp+X)
   IndexedXIndirect();
   IAPU.YA.B.A &= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu28()
{
   // AND A,#00
   IAPU.YA.B.A &= OP1;
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu29()
{
   // AND dp(dest),dp(src)
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   Work8 &= S9xAPUGetByteZ(OP2);
   S9xAPUSetByteZ(Work8, OP2);
   APUSetZN8(Work8);
   IAPU.PC += 3;
}

void Apu34()
{
   // AND A,dp+X
   IAPU.YA.B.A &= S9xAPUGetByteZ(OP1 + IAPU.X);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu35()
{
   // AND A,abs+X
   AbsoluteX();
   IAPU.YA.B.A &= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 3;
}

void Apu36()
{
   // AND A,abs+Y
   AbsoluteY();
   IAPU.YA.B.A &= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 3;
}

void Apu37()
{
   // AND A,(dp)+Y
   IndirectIndexedY();
   IAPU.YA.B.A &= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu38()
{
   // AND dp,#00
   uint8 Work8 = OP1;
   Work8 &= S9xAPUGetByteZ(OP2);
   S9xAPUSetByteZ(Work8, OP2);
   APUSetZN8(Work8);
   IAPU.PC += 3;
}

void Apu39()
{
   // AND (X),(Y)
   uint8 Work8 = S9xAPUGetByteZ(IAPU.X) & S9xAPUGetByteZ(IAPU.YA.B.Y);
   APUSetZN8(Work8);
   S9xAPUSetByteZ(Work8, IAPU.X);
   IAPU.PC++;
}

void Apu2B()
{
   // ROL dp
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   ROL(Work8);
   S9xAPUSetByteZ(Work8, OP1);
   IAPU.PC += 2;
}

void Apu2C()
{
   uint8 Work8;
   // ROL abs
   Absolute();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   ROL(Work8);
   S9xAPUSetByte(Work8, IAPU.Address);
   IAPU.PC += 3;
}

void Apu3B()
{
   // ROL dp+X
   uint8 Work8 = S9xAPUGetByteZ(OP1 + IAPU.X);
   ROL(Work8);
   S9xAPUSetByteZ(Work8, OP1 + IAPU.X);
   IAPU.PC += 2;
}

void Apu3C()
{
   // ROL A
   ROL(IAPU.YA.B.A);
   IAPU.PC++;
}

void Apu2E()
{
   // CBNE dp,rel
   uint8 Work8 = OP1;
   Relative2();

   if (S9xAPUGetByteZ(Work8) != IAPU.YA.B.A)
   {
      IAPU.PC = IAPU.RAM + (uint16) Int16;
      CPU.APU_Cycles += IAPU.TwoCycles;
      APUShutdown();
   }
   else
      IAPU.PC += 3;
}

void ApuDE()
{
   // CBNE dp+X,rel
   uint8 Work8 = OP1 + IAPU.X;
   Relative2();

   if (S9xAPUGetByteZ(Work8) != IAPU.YA.B.A)
   {
      IAPU.PC = IAPU.RAM + (uint16) Int16;
      CPU.APU_Cycles += IAPU.TwoCycles;
      APUShutdown();
   }
   else
      IAPU.PC += 3;
}

void Apu3D()
{
   // INC X
   IAPU.X++;
   APUSetZN8(IAPU.X);

#ifdef SPC700_SHUTDOWN
   IAPU.WaitCounter++;
#endif

   IAPU.PC++;
}

void ApuFC()
{
   // INC Y
   IAPU.YA.B.Y++;
   APUSetZN8(IAPU.YA.B.Y);

#ifdef SPC700_SHUTDOWN
   IAPU.WaitCounter++;
#endif

   IAPU.PC++;
}

void Apu1D()
{
   // DEC X
   IAPU.X--;
   APUSetZN8(IAPU.X);

#ifdef SPC700_SHUTDOWN
   IAPU.WaitCounter++;
#endif

   IAPU.PC++;
}

void ApuDC()
{
   // DEC Y
   IAPU.YA.B.Y--;
   APUSetZN8(IAPU.YA.B.Y);

#ifdef SPC700_SHUTDOWN
   IAPU.WaitCounter++;
#endif

   IAPU.PC++;
}

void ApuAB()
{
   // INC dp
   uint8 Work8 = S9xAPUGetByteZ(OP1) + 1;
   S9xAPUSetByteZ(Work8, OP1);
   APUSetZN8(Work8);

#ifdef SPC700_SHUTDOWN
   IAPU.WaitCounter++;
#endif

   IAPU.PC += 2;
}

void ApuAC()
{
   uint8 Work8;
   // INC abs
   Absolute();
   Work8 = S9xAPUGetByte(IAPU.Address) + 1;
   S9xAPUSetByte(Work8, IAPU.Address);
   APUSetZN8(Work8);

#ifdef SPC700_SHUTDOWN
   IAPU.WaitCounter++;
#endif

   IAPU.PC += 3;
}

void ApuBB()
{
   // INC dp+X
   uint8 Work8 = S9xAPUGetByteZ(OP1 + IAPU.X) + 1;
   S9xAPUSetByteZ(Work8, OP1 + IAPU.X);
   APUSetZN8(Work8);

#ifdef SPC700_SHUTDOWN
   IAPU.WaitCounter++;
#endif

   IAPU.PC += 2;
}

void ApuBC()
{
   // INC A
   IAPU.YA.B.A++;
   APUSetZN8(IAPU.YA.B.A);

#ifdef SPC700_SHUTDOWN
   IAPU.WaitCounter++;
#endif

   IAPU.PC++;
}

void Apu8B()
{
   // DEC dp
   uint8 Work8 = S9xAPUGetByteZ(OP1) - 1;
   S9xAPUSetByteZ(Work8, OP1);
   APUSetZN8(Work8);

#ifdef SPC700_SHUTDOWN
   IAPU.WaitCounter++;
#endif

   IAPU.PC += 2;
}

void Apu8C()
{
   uint8 Work8;
   // DEC abs
   Absolute();
   Work8 = S9xAPUGetByte(((IAPU.Address))) - 1;
   S9xAPUSetByte(Work8, IAPU.Address);
   APUSetZN8(Work8);

#ifdef SPC700_SHUTDOWN
   IAPU.WaitCounter++;
#endif

   IAPU.PC += 3;
}

void Apu9B()
{
   // DEC dp+X
   uint8 Work8 = S9xAPUGetByteZ(OP1 + IAPU.X) - 1;
   S9xAPUSetByteZ(Work8, OP1 + IAPU.X);
   APUSetZN8(Work8);

#ifdef SPC700_SHUTDOWN
   IAPU.WaitCounter++;
#endif

   IAPU.PC += 2;
}

void Apu9C()
{
   // DEC A
   IAPU.YA.B.A--;
   APUSetZN8(IAPU.YA.B.A);

#ifdef SPC700_SHUTDOWN
   IAPU.WaitCounter++;
#endif

   IAPU.PC++;
}

void Apu44()
{
   // EOR A,dp
   IAPU.YA.B.A ^= S9xAPUGetByteZ(OP1);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu45()
{
   // EOR A,abs
   Absolute();
   IAPU.YA.B.A ^= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 3;
}

void Apu46()
{
   // EOR A,(X)
   IAPU.YA.B.A ^= S9xAPUGetByteZ(IAPU.X);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC++;
}

void Apu47()
{
   // EOR A,(dp+X)
   IndexedXIndirect();
   IAPU.YA.B.A ^= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu48()
{
   // EOR A,#00
   IAPU.YA.B.A ^= OP1;
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu49()
{
   // EOR dp(dest),dp(src)
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   Work8 ^= S9xAPUGetByteZ(OP2);
   S9xAPUSetByteZ(Work8, OP2);
   APUSetZN8(Work8);
   IAPU.PC += 3;
}

void Apu54()
{
   // EOR A,dp+X
   IAPU.YA.B.A ^= S9xAPUGetByteZ(OP1 + IAPU.X);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu55()
{
   // EOR A,abs+X
   AbsoluteX();
   IAPU.YA.B.A ^= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 3;
}

void Apu56()
{
   // EOR A,abs+Y
   AbsoluteY();
   IAPU.YA.B.A ^= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 3;
}

void Apu57()
{
   // EOR A,(dp)+Y
   IndirectIndexedY();
   IAPU.YA.B.A ^= S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void Apu58()
{
   // EOR dp,#00
   uint8 Work8 = OP1;
   Work8 ^= S9xAPUGetByteZ(OP2);
   S9xAPUSetByteZ(Work8, OP2);
   APUSetZN8(Work8);
   IAPU.PC += 3;
}

void Apu59()
{
   // EOR (X),(Y)
   uint8 Work8 = S9xAPUGetByteZ(IAPU.X) ^ S9xAPUGetByteZ(IAPU.YA.B.Y);
   APUSetZN8(Work8);
   S9xAPUSetByteZ(Work8, IAPU.X);
   IAPU.PC++;
}

void Apu4B()
{
   // LSR dp
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   LSR(Work8);
   S9xAPUSetByteZ(Work8, OP1);
   IAPU.PC += 2;
}

void Apu4C()
{
   uint8 Work8;
   // LSR abs
   Absolute();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   LSR(Work8);
   S9xAPUSetByte(Work8, IAPU.Address);
   IAPU.PC += 3;
}

void Apu5B()
{
   // LSR dp+X
   uint8 Work8 = S9xAPUGetByteZ(OP1 + IAPU.X);
   LSR(Work8);
   S9xAPUSetByteZ(Work8, OP1 + IAPU.X);
   IAPU.PC += 2;
}

void Apu5C()
{
   // LSR A
   LSR(IAPU.YA.B.A);
   IAPU.PC++;
}

void Apu7D()
{
   // MOV A,X
   IAPU.YA.B.A = IAPU.X;
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC++;
}

void ApuDD()
{
   // MOV A,Y
   IAPU.YA.B.A = IAPU.YA.B.Y;
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC++;
}

void Apu5D()
{
   // MOV X,A
   IAPU.X = IAPU.YA.B.A;
   APUSetZN8(IAPU.X);
   IAPU.PC++;
}

void ApuFD()
{
   // MOV Y,A
   IAPU.YA.B.Y = IAPU.YA.B.A;
   APUSetZN8(IAPU.YA.B.Y);
   IAPU.PC++;
}

void Apu9D()
{
   //MOV X,SP
   IAPU.X = IAPU.S;
   APUSetZN8(IAPU.X);
   IAPU.PC++;
}

void ApuBD()
{
   // MOV SP,X
   IAPU.S = IAPU.X;
   IAPU.PC++;
}

void Apu6B()
{
   // ROR dp
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   ROR(Work8);
   S9xAPUSetByteZ(Work8, OP1);
   IAPU.PC += 2;
}

void Apu6C()
{
   uint8 Work8;
   // ROR abs
   Absolute();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   ROR(Work8);
   S9xAPUSetByte(Work8, IAPU.Address);
   IAPU.PC += 3;
}

void Apu7B()
{
   // ROR dp+X
   uint8 Work8 = S9xAPUGetByteZ(OP1 + IAPU.X);
   ROR(Work8);
   S9xAPUSetByteZ(Work8, OP1 + IAPU.X);
   IAPU.PC += 2;
}

void Apu7C()
{
   // ROR A
   ROR(IAPU.YA.B.A);
   IAPU.PC++;
}

void Apu6E()
{
   uint8 W1;
   // DBNZ dp,rel
   uint8 Work8 = OP1;
   Relative2();
   W1 = S9xAPUGetByteZ(Work8) - 1;
   S9xAPUSetByteZ(W1, Work8);
   if (W1 != 0)
   {
      IAPU.PC = IAPU.RAM + (uint16) Int16;
      CPU.APU_Cycles += IAPU.TwoCycles;
   }
   else
      IAPU.PC += 3;
}

void ApuFE()
{
   // DBNZ Y,rel
   Relative();
   IAPU.YA.B.Y--;
   if (IAPU.YA.B.Y != 0)
   {
      IAPU.PC = IAPU.RAM + (uint16) Int16;
      CPU.APU_Cycles += IAPU.TwoCycles;
   }
   else
      IAPU.PC += 2;
}

void Apu6F()
{
   // RET
   uint16 Work16;
   PopW(Work16);
   IAPU.PC = IAPU.RAM + Work16;
}

void Apu7F()
{
   // RETI
   // STOP ("RETI");
   uint16 Work16;
   Pop(IAPU.P);
   S9xAPUUnpackStatus();
   PopW(Work16);
   IAPU.PC = IAPU.RAM + Work16;
}

void Apu84()
{
   // ADC A,dp
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   ADC(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void Apu85()
{
   uint8 Work8;
   // ADC A, abs
   Absolute();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   ADC(IAPU.YA.B.A, Work8);
   IAPU.PC += 3;
}

void Apu86()
{
   // ADC A,(X)
   uint8 Work8 = S9xAPUGetByteZ(IAPU.X);
   ADC(IAPU.YA.B.A, Work8);
   IAPU.PC++;
}

void Apu87()
{
   uint8 Work8;
   // ADC A,(dp+X)
   IndexedXIndirect();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   ADC(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void Apu88()
{
   // ADC A,#00
   uint8 Work8 = OP1;
   ADC(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void Apu89()
{
   // ADC dp(dest),dp(src)
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   uint8 W1 = S9xAPUGetByteZ(OP2);
   ADC(W1, Work8);
   S9xAPUSetByteZ(W1, OP2);
   IAPU.PC += 3;
}

void Apu94()
{
   // ADC A,dp+X
   uint8 Work8 = S9xAPUGetByteZ(OP1 + IAPU.X);
   ADC(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void Apu95()
{
   uint8 Work8;
   // ADC A, abs+X
   AbsoluteX();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   ADC(IAPU.YA.B.A, Work8);
   IAPU.PC += 3;
}

void Apu96()
{
   uint8 Work8;
   // ADC A, abs+Y
   AbsoluteY();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   ADC(IAPU.YA.B.A, Work8);
   IAPU.PC += 3;
}

void Apu97()
{
   uint8 Work8;
   // ADC A, (dp)+Y
   IndirectIndexedY();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   ADC(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void Apu98()
{
   // ADC dp,#00
   uint8 Work8 = OP1;
   uint8 W1 = S9xAPUGetByteZ(OP2);
   ADC(W1, Work8);
   S9xAPUSetByteZ(W1, OP2);
   IAPU.PC += 3;
}

void Apu99()
{
   // ADC (X),(Y)
   uint8 W1 = S9xAPUGetByteZ(IAPU.X);
   uint8 Work8 = S9xAPUGetByteZ(IAPU.YA.B.Y);
   ADC(W1, Work8);
   S9xAPUSetByteZ(W1, IAPU.X);
   IAPU.PC++;
}

void Apu8D()
{
   // MOV Y,#00
   IAPU.YA.B.Y = OP1;
   APUSetZN8(IAPU.YA.B.Y);
   IAPU.PC += 2;
}

void Apu8F()
{
   // MOV dp,#00
   uint8 Work8 = OP1;
   S9xAPUSetByteZ(Work8, OP2);
   IAPU.PC += 3;
}

void Apu9E()
{
   // DIV YA,X
   if (IAPU.X == 0)
   {
      APUSetOverflow();
      IAPU.YA.B.Y = 0xff;
      IAPU.YA.B.A = 0xff;
   }
   else
   {
      uint8 Work8;
      APUClearOverflow();
      Work8 = IAPU.YA.W / IAPU.X;
      IAPU.YA.B.Y = IAPU.YA.W % IAPU.X;
      IAPU.YA.B.A = Work8;
   }
   // XXX How should Overflow, Half Carry, Zero and Negative flags be set??
   // APUSetZN16 (IAPU.YA.W);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC++;
}

void Apu9F()
{
   // XCN A
   IAPU.YA.B.A = (IAPU.YA.B.A >> 4) | (IAPU.YA.B.A << 4);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC++;
}

void ApuA4()
{
   // SBC A, dp
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   SBC(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void ApuA5()
{
   uint8 Work8;
   // SBC A, abs
   Absolute();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   SBC(IAPU.YA.B.A, Work8);
   IAPU.PC += 3;
}

void ApuA6()
{
   // SBC A, (X)
   uint8 Work8 = S9xAPUGetByteZ(IAPU.X);
   SBC(IAPU.YA.B.A, Work8);
   IAPU.PC++;
}

void ApuA7()
{
   uint8 Work8;
   // SBC A,(dp+X)
   IndexedXIndirect();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   SBC(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void ApuA8()
{
   // SBC A,#00
   uint8 Work8 = OP1;
   SBC(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void ApuA9()
{
   // SBC dp(dest), dp(src)
   uint8 Work8 = S9xAPUGetByteZ(OP1);
   uint8 W1 = S9xAPUGetByteZ(OP2);
   SBC(W1, Work8);
   S9xAPUSetByteZ(W1, OP2);
   IAPU.PC += 3;
}

void ApuB4()
{
   // SBC A, dp+X
   uint8 Work8 = S9xAPUGetByteZ(OP1 + IAPU.X);
   SBC(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void ApuB5()
{
   uint8 Work8;
   // SBC A,abs+X
   AbsoluteX();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   SBC(IAPU.YA.B.A, Work8);
   IAPU.PC += 3;
}

void ApuB6()
{
   uint8 Work8;
   // SBC A,abs+Y
   AbsoluteY();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   SBC(IAPU.YA.B.A, Work8);
   IAPU.PC += 3;
}

void ApuB7()
{
   uint8 Work8;
   // SBC A,(dp)+Y
   IndirectIndexedY();
   Work8 = S9xAPUGetByte(((IAPU.Address)));
   SBC(IAPU.YA.B.A, Work8);
   IAPU.PC += 2;
}

void ApuB8()
{
   // SBC dp,#00
   uint8 Work8 = OP1;
   uint8 W1 = S9xAPUGetByteZ(OP2);
   SBC(W1, Work8);
   S9xAPUSetByteZ(W1, OP2);
   IAPU.PC += 3;
}

void ApuB9()
{
   // SBC (X),(Y)
   uint8 W1 = S9xAPUGetByteZ(IAPU.X);
   uint8 Work8 = S9xAPUGetByteZ(IAPU.YA.B.Y);
   SBC(W1, Work8);
   S9xAPUSetByteZ(W1, IAPU.X);
   IAPU.PC++;
}

void ApuAF()
{
   // MOV (X)+, A
   S9xAPUSetByteZ(IAPU.YA.B.A, IAPU.X++);
   IAPU.PC++;
}

void ApuBE()
{
   // DAS
   if ((IAPU.YA.B.A & 0x0f) > 9 || !APUCheckHalfCarry())
      IAPU.YA.B.A -= 6;
   if (IAPU.YA.B.A > 0x9f || !IAPU._Carry)
   {
      IAPU.YA.B.A -= 0x60;
      APUClearCarry();
   }
   else
      APUSetCarry();
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC++;
}

void ApuBF()
{
   // MOV A,(X)+
   IAPU.YA.B.A = S9xAPUGetByteZ(IAPU.X++);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC++;
}

void ApuC0()
{
   // DI
   APUClearInterrupt();
   IAPU.PC++;
}

void ApuA0()
{
   // EI
   APUSetInterrupt();
   IAPU.PC++;
}

void ApuC4()
{
   // MOV dp,A
   S9xAPUSetByteZ(IAPU.YA.B.A, OP1);
   IAPU.PC += 2;
}

void ApuC5()
{
   // MOV abs,A
   Absolute();
   S9xAPUSetByte(IAPU.YA.B.A, IAPU.Address);
   IAPU.PC += 3;
}

void ApuC6()
{
   // MOV (X), A
   S9xAPUSetByteZ(IAPU.YA.B.A, IAPU.X);
   IAPU.PC++;
}

void ApuC7()
{
   // MOV (dp+X),A
   IndexedXIndirect();
   S9xAPUSetByte(IAPU.YA.B.A, IAPU.Address);
   IAPU.PC += 2;
}

void ApuC9()
{
   // MOV abs,X
   Absolute();
   S9xAPUSetByte(IAPU.X, IAPU.Address);
   IAPU.PC += 3;
}

void ApuCB()
{
   // MOV dp,Y
   S9xAPUSetByteZ(IAPU.YA.B.Y, OP1);
   IAPU.PC += 2;
}

void ApuCC()
{
   // MOV abs,Y
   Absolute();
   S9xAPUSetByte(IAPU.YA.B.Y, IAPU.Address);
   IAPU.PC += 3;
}

void ApuCD()
{
   // MOV X,#00
   IAPU.X = OP1;
   APUSetZN8(IAPU.X);
   IAPU.PC += 2;
}

void ApuCF()
{
   // MUL YA
   IAPU.YA.W = (uint16) IAPU.YA.B.A * IAPU.YA.B.Y;
   APUSetZN16(IAPU.YA.W);
   IAPU.PC++;
}

void ApuD4()
{
   // MOV dp+X, A
   S9xAPUSetByteZ(IAPU.YA.B.A, OP1 + IAPU.X);
   IAPU.PC += 2;
}

void ApuD5()
{
   // MOV abs+X,A
   AbsoluteX();
   S9xAPUSetByte(IAPU.YA.B.A, IAPU.Address);
   IAPU.PC += 3;
}

void ApuD6()
{
   // MOV abs+Y,A
   AbsoluteY();
   S9xAPUSetByte(IAPU.YA.B.A, IAPU.Address);
   IAPU.PC += 3;
}

void ApuD7()
{
   // MOV (dp)+Y,A
   IndirectIndexedY();
   S9xAPUSetByte(IAPU.YA.B.A, IAPU.Address);
   IAPU.PC += 2;
}

void ApuD8()
{
   // MOV dp,X
   S9xAPUSetByteZ(IAPU.X, OP1);
   IAPU.PC += 2;
}

void ApuD9()
{
   // MOV dp+Y,X
   S9xAPUSetByteZ(IAPU.X, OP1 + IAPU.YA.B.Y);
   IAPU.PC += 2;
}

void ApuDB()
{
   // MOV dp+X,Y
   S9xAPUSetByteZ(IAPU.YA.B.Y, OP1 + IAPU.X);
   IAPU.PC += 2;
}

void ApuDF()
{
   // DAA
   if ((IAPU.YA.B.A & 0x0f) > 9 || APUCheckHalfCarry())
   {
      if (IAPU.YA.B.A > 0xf0) APUSetCarry();
      IAPU.YA.B.A += 6;
      //APUSetHalfCarry (); Intel procs do this, but this is a Sony proc...
   }
   //else { APUClearHalfCarry (); } ditto as above
   if (IAPU.YA.B.A > 0x9f || IAPU._Carry)
   {
      IAPU.YA.B.A += 0x60;
      APUSetCarry();
   }
   else
      APUClearCarry();
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC++;
}

void ApuE4()
{
   // MOV A, dp
   IAPU.YA.B.A = S9xAPUGetByteZ(OP1);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void ApuE5()
{
   // MOV A,abs
   Absolute();
   IAPU.YA.B.A = S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 3;
}

void ApuE6()
{
   // MOV A,(X)
   IAPU.YA.B.A = S9xAPUGetByteZ(IAPU.X);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC++;
}

void ApuE7()
{
   // MOV A,(dp+X)
   IndexedXIndirect();
   IAPU.YA.B.A = S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void ApuE8()
{
   // MOV A,#00
   IAPU.YA.B.A = OP1;
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void ApuE9()
{
   // MOV X, abs
   Absolute();
   IAPU.X = S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.X);
   IAPU.PC += 3;
}

void ApuEB()
{
   // MOV Y,dp
   IAPU.YA.B.Y = S9xAPUGetByteZ(OP1);
   APUSetZN8(IAPU.YA.B.Y);
   IAPU.PC += 2;
}

void ApuEC()
{
   // MOV Y,abs
   Absolute();
   IAPU.YA.B.Y = S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.Y);
   IAPU.PC += 3;
}

void ApuF4()
{
   // MOV A, dp+X
   IAPU.YA.B.A = S9xAPUGetByteZ(OP1 + IAPU.X);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void ApuF5()
{
   // MOV A, abs+X
   AbsoluteX();
   IAPU.YA.B.A = S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 3;
}

void ApuF6()
{
   // MOV A, abs+Y
   AbsoluteY();
   IAPU.YA.B.A = S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 3;
}

void ApuF7()
{
   // MOV A, (dp)+Y
   IndirectIndexedY();
   IAPU.YA.B.A = S9xAPUGetByte(IAPU.Address);
   APUSetZN8(IAPU.YA.B.A);
   IAPU.PC += 2;
}

void ApuF8()
{
   // MOV X,dp
   IAPU.X = S9xAPUGetByteZ(OP1);
   APUSetZN8(IAPU.X);
   IAPU.PC += 2;
}

void ApuF9()
{
   // MOV X,dp+Y
   IAPU.X = S9xAPUGetByteZ(OP1 + IAPU.YA.B.Y);
   APUSetZN8(IAPU.X);
   IAPU.PC += 2;
}

void ApuFA()
{
   // MOV dp(dest),dp(src)
   S9xAPUSetByteZ(S9xAPUGetByteZ(OP1), OP2);
   IAPU.PC += 3;
}

void ApuFB()
{
   // MOV Y,dp+X
   IAPU.YA.B.Y = S9xAPUGetByteZ(OP1 + IAPU.X);
   APUSetZN8(IAPU.YA.B.Y);
   IAPU.PC += 2;
}

#if defined(NO_INLINE_SET_GET)
#undef INLINE
#define INLINE
#include "apumem.h"
#endif


void (*S9xApuOpcodes[256])(void) =
{
   Apu00, Apu01, Apu02, Apu03, Apu04, Apu05, Apu06, Apu07,
   Apu08, Apu09, Apu0A, Apu0B, Apu0C, Apu0D, Apu0E, Apu0F,
   Apu10, Apu11, Apu12, Apu13, Apu14, Apu15, Apu16, Apu17,
   Apu18, Apu19, Apu1A, Apu1B, Apu1C, Apu1D, Apu1E, Apu1F,
   Apu20, Apu21, Apu22, Apu23, Apu24, Apu25, Apu26, Apu27,
   Apu28, Apu29, Apu2A, Apu2B, Apu2C, Apu2D, Apu2E, Apu2F,
   Apu30, Apu31, Apu32, Apu33, Apu34, Apu35, Apu36, Apu37,
   Apu38, Apu39, Apu3A, Apu3B, Apu3C, Apu3D, Apu3E, Apu3F,
   Apu40, Apu41, Apu42, Apu43, Apu44, Apu45, Apu46, Apu47,
   Apu48, Apu49, Apu4A, Apu4B, Apu4C, Apu4D, Apu4E, Apu4F,
   Apu50, Apu51, Apu52, Apu53, Apu54, Apu55, Apu56, Apu57,
   Apu58, Apu59, Apu5A, Apu5B, Apu5C, Apu5D, Apu5E, Apu5F,
   Apu60, Apu61, Apu62, Apu63, Apu64, Apu65, Apu66, Apu67,
   Apu68, Apu69, Apu6A, Apu6B, Apu6C, Apu6D, Apu6E, Apu6F,
   Apu70, Apu71, Apu72, Apu73, Apu74, Apu75, Apu76, Apu77,
   Apu78, Apu79, Apu7A, Apu7B, Apu7C, Apu7D, Apu7E, Apu7F,
   Apu80, Apu81, Apu82, Apu83, Apu84, Apu85, Apu86, Apu87,
   Apu88, Apu89, Apu8A, Apu8B, Apu8C, Apu8D, Apu8E, Apu8F,
   Apu90, Apu91, Apu92, Apu93, Apu94, Apu95, Apu96, Apu97,
   Apu98, Apu99, Apu9A, Apu9B, Apu9C, Apu9D, Apu9E, Apu9F,
   ApuA0, ApuA1, ApuA2, ApuA3, ApuA4, ApuA5, ApuA6, ApuA7,
   ApuA8, ApuA9, ApuAA, ApuAB, ApuAC, ApuAD, ApuAE, ApuAF,
   ApuB0, ApuB1, ApuB2, ApuB3, ApuB4, ApuB5, ApuB6, ApuB7,
   ApuB8, ApuB9, ApuBA, ApuBB, ApuBC, ApuBD, ApuBE, ApuBF,
   ApuC0, ApuC1, ApuC2, ApuC3, ApuC4, ApuC5, ApuC6, ApuC7,
   ApuC8, ApuC9, ApuCA, ApuCB, ApuCC, ApuCD, ApuCE, ApuCF,
   ApuD0, ApuD1, ApuD2, ApuD3, ApuD4, ApuD5, ApuD6, ApuD7,
   ApuD8, ApuD9, ApuDA, ApuDB, ApuDC, ApuDD, ApuDE, ApuDF,
   ApuE0, ApuE1, ApuE2, ApuE3, ApuE4, ApuE5, ApuE6, ApuE7,
   ApuE8, ApuE9, ApuEA, ApuEB, ApuEC, ApuED, ApuEE, ApuEF,
   ApuF0, ApuF1, ApuF2, ApuF3, ApuF4, ApuF5, ApuF6, ApuF7,
   ApuF8, ApuF9, ApuFA, ApuFB, ApuFC, ApuFD, ApuFE, ApuFF
};


