extern int Draw_Frame;

#ifdef SAVE_MEM
#include "lz.h"
//#warning Memsaving version
#else
#include "mem.c"
//#warning Preloading version
#endif

// The big delay at start up under SDL may be due to lack of cycle counting and all that that entails

// This is the last checked-in MSPac.c before the one where I noticed that
// the SDL graphics were missing. (MSPac-latest-broken.c)

// There is also MSPac-atora.c which was the last compiled version on
// a Pi.  Not sure at this point if that version included working SDL graphics
// or not.  I think X86 linux worked with SDL2 but Pi (arm) only worked with SDL

/* -*- tab-width: 4 -*- */
/******************************************************************************
 *
 *	Copyright 2004 The Orion Compiler Group. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 *modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE ORION COMPILER GROUP ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE ORION COMPILER GROUP OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the Orion Compiler Group.
 *
 ******************************************************************************/

/**************************************************************************
	MS Pacman support code
 **************************************************************************/

// For the curious, tabs are set to 1, 5, 9, etc... Do not modify and resubmit
// with spaces. It makes the code harder to merge in changes.

#include <time.h>
#include "types.h"
#include "memory.h"
#include "mspacman.h"
#include "z80stub.h"
#include "ncurses.h"   // KEY_UP etc
#include "graphics.h"  // interface-dependent graphics for main application.

#include "gfx.h"
#ifdef USE_SDL_GRAPHICS
#define _SDL_main_h
#include "SDL.h"
#endif
#ifdef USE_SDL2_GRAPHICS
#include <SDL2/SDL.h>
#include <SDL2/SDL_keycode.h>
#endif

#ifdef USE_NCURSES_GRAPHICS
#include "ncurses.h"   // KEY_UP etc
#include "graphics.h"  // interface-dependent graphics for main application.
#endif

#include "osdep.h"
#include "controls.h"

// Can't do step comparisons if either emulation isn't compiled in

// Pacman sprite/char decode structures

#if defined(USE_SDL_GRAPHICS) || defined(USE_SDL2_GRAPHICS)
static struct SRasterDecode sg_sPacChars = {DGF_NORMAL,
                                            8,
                                            8,
                                            256,
                                            16 * 8,
                                            2,
                                            {0, 4},
                                            2,
                                            {7 * 8, 6 * 8, 5 * 8, 4 * 8, 3 * 8, 2 * 8, 1 * 8, 0 * 8},
                                            {8 * 8 + 0, 8 * 8 + 1, 8 * 8 + 2, 8 * 8 + 3, 0, 1, 2, 3},
                                            1,
                                            1,
                                            {0x0},
                                            0,
                                            NULL};

static UINT8 bPacTransparency[4] = {0, 1, 1, 1};

static struct SRasterDecode sg_sPacSprites = {
    DGF_TRANSPARENT,
    16,
    16,
    64,
    64 * 8,
    2,
    {0, 4},
    2,
    {39 * 8, 38 * 8, 37 * 8, 36 * 8, 35 * 8, 34 * 8, 33 * 8, 32 * 8, 7 * 8, 6 * 8, 5 * 8, 4 * 8, 3 * 8, 2 * 8, 1 * 8,
     0 * 8},
    {8 * 8, 8 * 8 + 1, 8 * 8 + 2, 8 * 8 + 3, 16 * 8 + 0, 16 * 8 + 1, 16 * 8 + 2, 16 * 8 + 3, 24 * 8 + 0, 24 * 8 + 1,
     24 * 8 + 2, 24 * 8 + 3, 0, 1, 2, 3},
    1,
    1,
    {0x0},
    0,
    bPacTransparency};

static struct SImage *sg_psPacChars = NULL;
static struct SImage *sg_psPacSprites = NULL;
#endif

#ifndef TRUE
#define TRUE 0x01
#endif

#ifndef FALSE
#define FALSE 0x00
#endif

// Memory/IO read/write commands

#ifndef VALUE_BYTE
#define VALUE_BYTE 0
#endif

#ifndef VALUE_WORD
#define VALUE_WORD 1
#endif

#ifndef VALUE_DWORD
#define VALUE_DWORD 2
#endif

#ifndef VALUE_IO
#define VALUE_IO 3
#endif

// Flag definitions

#define Z80_FLAG_CARRY 0x01
#define Z80_FLAG_NEGATIVE 0x02
#define Z80_FLAG_OVERFLOW_PARITY 0x04
#define Z80_FLAG_UNDEFINED1 0x08
#define Z80_FLAG_HALF_CARRY 0x10
#define Z80_FLAG_UNDEFINED2 0x20
#define Z80_FLAG_ZERO 0x40
#define Z80_FLAG_SIGN 0x80

#define IFF1 0x01
#define IFF2 0x02

typedef struct mz80context CONTEXTMZ80;

// 3.072Mhz CPU, one IRQ (if enabled) per frame

#define TSTATES_PER_FRAME (3070000 / 60)  // changing this does not appear to affect how fast the game is played either

UINT8 *sg_pu8RecompBase = NULL;
//static UINT8 *sg_u8EmulationBase = NULL;
static UINT8 sg_u8PacDirty[0x400];

static BOOL sg_bMSIntEnabled = FALSE;
static BOOL sg_bMSSoundEnabled = FALSE;
static BOOL sg_bMSFlipScreen = FALSE;
static BOOL sg_bMSPacmanRun = TRUE;
static UINT8 sg_u8MSPacIntAddr = 0;

void MSNothingWrite(UINT32 u32Address, UINT8 u8Data) {}

void MSNothingWrite2(UINT32 u32Address, UINT8 u8Data, struct MemoryWriteByte *pMemWrite) {}

void MSStuffWrite(UINT32 u32Address, UINT8 u8Data) {
  if (0x5000 == u32Address) {
    sg_bMSIntEnabled = (BOOL)u8Data;
  } else if (0x5001 == u32Address) {
    sg_bMSSoundEnabled = TRUE;
  } else if (0x5003 == u32Address) {
    sg_bMSFlipScreen = (BOOL)(u8Data & 1);
  } else if (u32Address >= 0x5040 && u32Address <= 0x505f) {
    // Sound write!
  }
}

void MSStuffWrite2(UINT32 u32Address, UINT8 u8Data, struct MemoryWriteByte *pMemWrite) {
  if (0x5000 == u32Address) {
    sg_bMSIntEnabled = (BOOL)u8Data;
  } else if (0x5001 == u32Address) {
    sg_bMSSoundEnabled = TRUE;
  } else if (0x5003 == u32Address) {
    sg_bMSFlipScreen = (BOOL)(u8Data & 1);
  } else if (u32Address >= 0x5040 && u32Address <= 0x505f) {
    // Sound write!
  }
}

void MSPacVideoWrite(UINT32 u32Address, UINT8 u8Data) {
  u32Address = 0x4000 + (u32Address & 0x7ff);
  if (sg_pu8RecompBase[u32Address] != u8Data) {
    // Only bother to write deltas

    sg_pu8RecompBase[u32Address] = u8Data;
    sg_u8PacDirty[(u32Address & 0x3ff)] = 1;
  }
}

void MSPacVideoWrite2(UINT32 u32Address, UINT8 u8Data, struct MemoryWriteByte *pMemWrite) {
  u32Address = 0x4000 + (u32Address & 0x7ff);
  if (sg_pu8RecompBase[u32Address] != u8Data) {
    // Only bother to write deltas

    sg_pu8RecompBase[u32Address] = u8Data;
    sg_u8PacDirty[(u32Address & 0x3ff)] = 1;
  }
}

void MSPacIntWrite(UINT16 u16Address, UINT8 u8Data, struct z80PortWrite *psPortWrite) {
  sg_u8MSPacIntAddr = u8Data;
}

void MSIOWrite(UINT32 u32Address, UINT8 u8Data) { sg_u8MSPacIntAddr = u8Data; }

#ifdef NEVER
static BOOL sg_bIntEnabled = FALSE;
static BOOL sg_bSoundEnabled = FALSE;
static BOOL sg_bFlipScreen = FALSE;
static UINT8 sg_u8PacIntAddr = 0;

/**/ static void PacVideoWrite(UINT32 u32Address, UINT8 u8Data, struct MemoryWriteByte *psMemWrite) {
  u32Address = 0x4000 + (u32Address & 0x7ff);
  if (sg_u8EmulationBase[u32Address] != u8Data) {
    // Only bother to write deltas

    sg_u8EmulationBase[u32Address] = u8Data;
    sg_u8PacDirty[(u32Address & 0x3ff)] = 1;
  }
}

/**/ static void StuffWrite(UINT32 u32Address, UINT8 u8Data, struct MemoryWriteByte *psMemWrite) {
  if (0x5000 == u32Address) {
    sg_bIntEnabled = (BOOL)u8Data;
  } else if (0x5001 == u32Address) {
    sg_bSoundEnabled = TRUE;
  } else if (0x5003 == u32Address) {
    sg_bFlipScreen = (BOOL)(u8Data & 1);
  } else if (u32Address >= 0x5040 && u32Address <= 0x505f) {
    // Sound write!
  }
}

/**/ static void NothingWrite(UINT32 u32Address, UINT8 u8Data, struct MemoryWriteByte *pMemWrite) {}

/**/ static void PacIntWrite(UINT16 u16Address, UINT8 u8Data, struct z80PortWrite *psPortWrite) {
  sg_u8PacIntAddr = u8Data;
}
#endif

struct MemoryWriteByte sPacWrite[] = {{0x4000, 0x47ff, MSPacVideoWrite2},
                                      {0xc000, 0xc7ff, MSPacVideoWrite2},
                                      {0x5000, 0x505f, MSStuffWrite2},
                                      {0x507f, 0x50ff, MSNothingWrite2},
                                      {-1, -1, NULL}};

struct MemoryReadByte sPacRead[] = {{-1, -1, NULL}};

#ifdef SAVE_MEM
static BOOL LoadRom(UINT8 *pu8Filename, UINT8 **pu8LoadAddress) {
  FILE *fp;
  UINT32 u32Size = 0;

  fp = fopen(pu8Filename, "rb");

  if (NULL == fp) {
    return (FALSE);
  }

  fseek(fp, 0, SEEK_END);
  u32Size = ftell(fp);
  fseek(fp, 0, SEEK_SET);

  if (NULL == *pu8LoadAddress) {
    *pu8LoadAddress = MyMalloc(u32Size);
  }

  fread(*pu8LoadAddress, 1, u32Size, fp);
  fclose(fp);

  return (TRUE);
}
#endif

#if defined(USE_SDL_GRAPHICS) || defined(USE_SDL2_GRAPHICS)
struct SColorMap {
  UINT8 u8Red;
  UINT8 u8Green;
  UINT8 u8Blue;
};

static void PacConvertColorPROM(UINT8 *pu8ColorPROM) {
  UINT8 u8Loop;
  UINT8 bBit0, bBit1, bBit2;
  struct SColorMap sPalette[16];

  for (u8Loop = 0; u8Loop < 16; u8Loop++) {
    bBit0 = (*pu8ColorPROM) & 0x01;
    bBit1 = (*pu8ColorPROM >> 1) & 0x01;
    bBit2 = (*pu8ColorPROM >> 2) & 0x01;
    sPalette[u8Loop].u8Red = (0x21 * bBit0) + (0x47 * bBit1) + (0x97 * bBit2);

    bBit0 = (*pu8ColorPROM >> 3) & 0x01;
    bBit1 = (*pu8ColorPROM >> 4) & 0x01;
    bBit2 = (*pu8ColorPROM >> 5) & 0x01;
    sPalette[u8Loop].u8Green = (0x21 * bBit0) + (0x47 * bBit1) + (0x97 * bBit2);

    bBit0 = 0;
    bBit1 = (*pu8ColorPROM >> 6) & 0x01;
    bBit2 = (*pu8ColorPROM >> 7) & 0x01;
    sPalette[u8Loop].u8Blue = (0x47 * bBit1) + (0x97 * bBit2);

    pu8ColorPROM++;
  }

  for (u8Loop = 0; u8Loop < 128; u8Loop++) {
    GfxSetPaletteEntry(u8Loop, sPalette[*pu8ColorPROM].u8Red, sPalette[*pu8ColorPROM].u8Green,
                               sPalette[*pu8ColorPROM].u8Blue);
    pu8ColorPROM++;
  }
}

static UINT8 sg_u8PacColorPROM[] = {
    0x00, 0x07, 0x66, 0xEF, 0x00, 0xF8, 0xEA, 0x6F, 0x00, 0x3F, 0x00, 0xC9, 0x38, 0xAA, 0xAF, 0xF6, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x0F, 0x0B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0B, 0x03, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x0F, 0x0B, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0B, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B,
    0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x0F, 0x00, 0x0E, 0x00, 0x01, 0x0C, 0x0F, 0x00, 0x0E, 0x00, 0x0B, 0x00, 0x0C, 0x0B, 0x0E, 0x00, 0x0C,
    0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x0F, 0x00, 0x07, 0x0C, 0x02, 0x00, 0x09, 0x06, 0x0F,
    0x00, 0x0D, 0x0C, 0x0F, 0x00, 0x05, 0x03, 0x09, 0x00, 0x0F, 0x0B, 0x00, 0x00, 0x0E, 0x00, 0x0B, 0x00, 0x0E,
    0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0E, 0x01, 0x00, 0x0F, 0x0B, 0x0E, 0x00, 0x0E, 0x00, 0x0F};
#endif // SDL

#ifdef USE_NCURSES_GRAPHICS
#include "ncurses.h"
#endif

static void MSPacmanDrawFrame(BOOL bDoDraw) {
#ifdef USE_NCURSES_GRAPHICS
  // #define getmaxyx(win,y,x)       (y = getmaxy(win), x = getmaxx(win))
  int X, Y;
  getmaxyx(stdscr, Y, X);
  graphics_redraw((X - 55) / 2, (Y - 35) / 2);  // TO DO: center in ncurses
#else
  UINT16 i;
  UINT32 u32X, u32Y, u32VirtX, u32VirtY;
  UINT8 bYPos = 0;
  UINT8 bXPos = 0;
  UINT8 *pbDirtyTrousers = NULL;
  UINT8 *pbSpriteInfo;
  UINT8 *pbSpritePos;
  struct SGfx sGraphics;

  if (bDoDraw) {
    memset(&sGraphics, 0, sizeof(sGraphics));

    sGraphics.psImage = sg_psPacChars;
    sGraphics.u32XSize = 8;
    sGraphics.u32YSize = 8;

    for (i = 0; i < 0x400; i++) {
      if (sg_u8PacDirty[i]) {
        u32VirtX = i >> 5;
        u32VirtY = i & 0x1f;

        // This takes the rightmost two columns and puts them on the top
        // 2 and bottom 2 lines.

        if (u32VirtX < 2) {
          u32X = 31 - u32VirtY;
          u32Y = u32VirtX + 34;
        } else if (u32VirtX > 29) {
          u32X = 31 - u32VirtY;
          u32Y = u32VirtX - 30;
        } else {
          u32X = 31 - u32VirtX;
          u32Y = u32VirtY + 2;
        }

        // Make sure we're not drawing off the left edge of the screen

        sGraphics.u32Offset = sg_pu8RecompBase[0x4000 + i];
        sGraphics.u32Color = sg_pu8RecompBase[0x4400 + i] & 0x1f;
        sGraphics.u32XPos = (u32X << 3);
        sGraphics.u32YPos = (u32Y << 3) + 16;

        GfxDraw(&sGraphics);

        sg_u8PacDirty[i] = 0;
      }
    }

    // Set up sprite structures

    pbSpriteInfo = (UINT8 *)(sg_pu8RecompBase + 0x4ffd);
    pbSpritePos = (UINT8 *)(sg_pu8RecompBase + 0x506d);

    sGraphics.u32XSize = 16;
    sGraphics.u32YSize = 16;
    sGraphics.psImage = sg_psPacSprites;

    for (i = 6; i > 0; i--) {
      bYPos = *pbSpritePos--;
      bXPos = *pbSpritePos--;

      sGraphics.u32Color = *pbSpriteInfo--;
      sGraphics.u8Orientation = *pbSpriteInfo & 3;
      sGraphics.u32Offset = *pbSpriteInfo-- >> 2;

      // don't draw the all black sprite

      if ((272 - bYPos > 2 * 8) && (272 - bYPos < 33 * 8) && sGraphics.u32Color) {
        // the last two sprites are set one pixel to the left...

        if (i < 3)
          u32X = 1;
        else
          u32X = 0;

        sGraphics.u32XPos = (255 - bXPos - u32X);
        sGraphics.u32YPos = (288 - bYPos);

        if (sGraphics.u32XPos < 256 && sGraphics.u32YPos <= 288) {
          GfxDraw(&sGraphics);

          // Now we need to dirty the tiles underneath this sprite!

          sGraphics.u32YPos -= 32;
          sGraphics.u32XPos = 241 - sGraphics.u32XPos;

          if (sGraphics.u32XPos < 256 && sGraphics.u32YPos < 255 && sGraphics.u32XPos) {
            // Dirty the tiles underneath the sprites

            pbDirtyTrousers = &sg_u8PacDirty[(((sGraphics.u32XPos) >> 3) << 5) + ((sGraphics.u32YPos) >> 3)];
            *pbDirtyTrousers = 1;
            *(pbDirtyTrousers + 1) = 1;
            *(pbDirtyTrousers + 2) = 1;
            pbDirtyTrousers += 0x20;
            *pbDirtyTrousers = 2;
            *(pbDirtyTrousers + 1) = 1;
            *(pbDirtyTrousers + 2) = 1;

            // Our X and Y got swapped. If we're off the display, let's not clobber the dirty buffer

            if (sGraphics.u32XPos < 240) {
              pbDirtyTrousers += 0x20;
              *pbDirtyTrousers = 1;
              *(pbDirtyTrousers + 1) = 1;
              *(pbDirtyTrousers + 2) = 1;
            }
          }
        }
      }
    }

    // Go display things.

    GfxBlit();
  }
#endif
}

static void MSPacmanExit(void) { sg_bMSPacmanRun = FALSE; }

/*static*/ /*shared with mz80.c*/ UINT8 *pbAddAdcTable; /* Pointer to add/adc flag table */
/*static*/ /*shared with mz80.c*/ UINT8 *pbSubSbcTable; /* Pointer to sub/sbc flag table */

//CONTEXTMZ80 cpu;     /* CPU Context */

/* Initialize MZ80 for action */

void mz80init(void) {
  UINT32 u32Loop;
  UINT8 *pbTempPtr;
  UINT8 *pbTempPtr2;
  UINT8 bNewAdd;
  UINT8 bNewSub;
  UINT8 bFlag;
  UINT8 bLow;
  UINT8 bHigh;
  UINT8 bCarry;

  if (NULL == pbAddAdcTable) {
    pbAddAdcTable = malloc(256 * 256 * 2);

    if (NULL == pbAddAdcTable) {
      return;
    }

    pbTempPtr = pbAddAdcTable;

    pbSubSbcTable = malloc(256 * 256 * 2);

    if (NULL == pbSubSbcTable) {
      return;
    }

    pbTempPtr2 = pbSubSbcTable;

    for (u32Loop = 0; u32Loop < (256 * 256 * 2); u32Loop++) {
      bLow = u32Loop & 0xff;
      bHigh = (u32Loop >> 8) & 0xff;
      bCarry = (u32Loop >> 16);

      bFlag = 0;
      bNewAdd = bHigh + bLow + bCarry;

      if (0 == bNewAdd) {
        bFlag |= Z80_FLAG_ZERO;
      } else {
        bFlag = bNewAdd & 0x80; /* Sign flag */
      }

      if (((UINT32)bLow + (UINT32)bHigh + (UINT32)bCarry) >= 0x100) {
        bFlag |= Z80_FLAG_CARRY;
      }

      if (((bLow ^ bHigh ^ 0x80) & (bLow ^ (bNewAdd & 0x80))) & 0x80) {
        bFlag |= Z80_FLAG_OVERFLOW_PARITY;
      }

      if (((bLow & 0x0f) + (bHigh & 0x0f) + bCarry) >= 0x10) {
        bFlag |= Z80_FLAG_HALF_CARRY;
      }

      *pbTempPtr++ = bFlag; /* Store our new flag */

      // Now do subtract - Zero

      bFlag = Z80_FLAG_NEGATIVE;
      bNewSub = bHigh - bLow - bCarry;

      if (0 == bNewSub) {
        bFlag |= Z80_FLAG_ZERO;
      } else {
        bFlag |= bNewSub & 0x80; /* Sign flag */
      }

      if (((INT32)bHigh - (INT32)bLow - (INT32)bCarry) < 0) {
        bFlag |= Z80_FLAG_CARRY;
      }

      if (((INT32)(bHigh & 0xf) - (INT32)(bLow & 0x0f) - (INT32)bCarry) < 0) {
        bFlag |= Z80_FLAG_HALF_CARRY;
      }

      if (((bLow ^ bHigh) & (bHigh ^ bNewSub) & 0x80)) {
        bFlag |= Z80_FLAG_OVERFLOW_PARITY;
      }

      *pbTempPtr2++ = bFlag; /* Store our sub flag */
    }
  }
}

/* Set mz80's context - placing here so we don't need to link in mz80.o */
#ifdef NEVER
static void mz80SetContext(void *pData) { }

/* This routine is mz80's reset handler */

static void mz80reset(void) { }
#endif

int main(int argc, char **argv) {
  UINT32 u32ThisCycles = 0;
  UINT32 u32CreditDebit = 0;
  UINT32 u32FrameCount = 0;
  UINT32 u32ExecResult;
#if defined(USE_SDL_GRAPHICS) || defined(USE_SDL2_GRAPHICS)
  BOOL bAnother = FALSE;
  BOOL bMonitor = FALSE;
#endif
  time_t u32LastTime = 0;
  struct z80context recompContext;
  //CONTEXTMZ80 mz80;
#if defined(SAVE_MEM) || !defined(USE_NCURSES_GRAPHICS)
  UINT8 *pu8GfxSrc = NULL;
#endif
  BOOL bDoBlit = TRUE;
#if defined(USE_SDL_GRAPHICS) || defined(USE_SDL2_GRAPHICS)
  struct SControllerAction sCtrl;
#endif
#ifdef SAVE_MEM
  UINT8 *pu8Addr;
#endif
  UINT32 u32RecompPriorPC = 0xffffffff;

  if (argc > 1) {
    if (strcmp(argv[1], "-nogfx") == 0) {
      bDoBlit = FALSE;
    }
  }

#ifdef SAVE_MEM
  pu8GfxSrc = MyMalloc(8192);
  if (FALSE == LoadRom("roms/5e", &pu8GfxSrc)) {
    printf("Can't find MS Pacman rom '5e'\n");
    exit(1);
  }

  pu8GfxSrc += 4096;

  if (FALSE == LoadRom("roms/5f", &pu8GfxSrc)) {
    printf("Can't find MS Pacman rom '5f'\n");
    exit(1);
  }

  pu8GfxSrc -= 4096;
#endif
  
#ifdef USE_NCURSES_GRAPHICS
  graphics_init();  // application graphics output
#else

  // Set up the graphics engine

  GfxInit();  // SDL graphics used for debugging

  // Decode characters

  GfxDecode(&sg_sPacChars, pu8GfxSrc, &sg_psPacChars);

  GfxDecode(&sg_sPacSprites, pu8GfxSrc + 0x1000, &sg_psPacSprites);

  // Set up the backbuffer

  GfxSetBackBuffer(224, 288, 16, 16, 8);  // 224x288, 16x16 overscan, and 8bpp

  // Set up a video display surface

  GfxSetDisplaySurface(224, 288, 8, TRUE);  // 224x288 or nearest resolution, 8bpp, windowed (if possible)

  // Set up the palette

  GfxSetPaletteSize(128);

  // Now set up the palette

  PacConvertColorPROM(sg_u8PacColorPROM);

  // Get rid of our graphics structure source

  GfxSetFrameRate(60);  // changing this does not affect how fast the game plays
                        // It may possibly affect how often the screen is refreshed

  MyFree((void **)&pu8GfxSrc);
#endif
  
  mspacman_init();   // Go initialize the memory
  mspacman_reset();  // Reset the virtual CPU

  // Initialize the emulator

  //memset((void *)&mz80, 0, sizeof(mz80));
  mz80init();

#ifdef SAVE_MEM
  sg_pu8RecompBase = mem;//malloc(0x10000);
  memset((void *)sg_pu8RecompBase, 0, 0x10000);
  //sg_u8EmulationBase = sg_pu8RecompBase;

  if (FALSE == LoadRom("roms/boot1", &sg_pu8RecompBase)) {
    printf("Can't open boot1\n");
    exit(1);
  }

  pu8Addr = sg_pu8RecompBase + 0x1000;
  if (FALSE == LoadRom("roms/boot2", &pu8Addr)) {
    printf("Can't open boot2\n");
    exit(1);
  }

  pu8Addr = sg_pu8RecompBase + 0x2000;
  if (FALSE == LoadRom("roms/boot3", &pu8Addr)) {
    printf("Can't open boot3\n");
    exit(1);
  }

  pu8Addr = sg_pu8RecompBase + 0x3000;
  if (FALSE == LoadRom("roms/boot4", &pu8Addr)) {
    printf("Can't open boot4\n");
    exit(1);
  }

  pu8Addr = sg_pu8RecompBase + 0x8000;
  if (FALSE == LoadRom("roms/boot5", &pu8Addr)) {
    printf("Can't open boot5\n");
    exit(1);
  }

  pu8Addr = sg_pu8RecompBase + 0x9000;
  if (FALSE == LoadRom("roms/boot6", &pu8Addr)) {
    printf("Can't open boot1\n");
    exit(1);
  }

#ifdef NEVER
  //mz80SetContext(&mz80);
  mz80reset();
#endif
  
  mspacman_GetCPUContext(&recompContext);
  sg_pu8RecompBase = recompContext.Base;
  mspacman_SetCPUContext(&recompContext);

  sg_pu8RecompBase[0x5000] = 0xff;
  sg_pu8RecompBase[0x5040] = 0xff;
  sg_pu8RecompBase[0x5080] = 0xc9;
  memset((void *)&sg_pu8RecompBase[0x5080], 0xe9, 0x40); // Huh? Overrides the line above...

  savemem(sg_pu8RecompBase);  // savemem(mem) ???
#else
  sg_pu8RecompBase = mem;//malloc(0x10000);
  //sg_u8EmulationBase = sg_pu8RecompBase;
#ifdef NEVER
  //mz80SetContext(&mz80);
  mz80reset();
#endif
  mspacman_GetCPUContext(&recompContext);
  sg_pu8RecompBase = recompContext.Base;
  mspacman_SetCPUContext(&recompContext);

  restore_db31fdad(mem);
#endif
  
  u32LastTime = time(0);
  sg_bMSPacmanRun = TRUE;
  HostSetGameExitCallbackProcedure(MSPacmanExit);

  // The ascii version does not use the SDL keystroke handling.
  // Much of this code can be removed for the ascii code.
  // Also the ascii version handles its own frame timing, so
  // we need to be careful not to apply two separate sets of
  // delays.
  
  // Install credit button

#if defined(USE_SDL_GRAPHICS) || defined(USE_SDL2_GRAPHICS)
  sCtrl.pu8Address = &sg_pu8RecompBase[0x5000];

  sCtrl.key = SDLK_3;
  sCtrl.u8Mask = ~0x20;
  sCtrl.u8Make = 0;
  sCtrl.u8Break = 0x20;
  ControlInstallAssignment(&sCtrl);
  sCtrl.pu8Address = sg_pu8RecompBase + 0x5000;
  ControlInstallAssignment(&sCtrl);

  // Install player 1 start

  sCtrl.pu8Address = &sg_pu8RecompBase[0x5040];
  sCtrl.key = SDLK_1;
  sCtrl.u8Mask = ~0x20;
  sCtrl.u8Make = 0;
  sCtrl.u8Break = 0x20;
  ControlInstallAssignment(&sCtrl);
  sCtrl.pu8Address = sg_pu8RecompBase + 0x5040;
  ControlInstallAssignment(&sCtrl);

  sCtrl.key = SDLK_2;
  sCtrl.u8Mask = ~0x40;
  sCtrl.u8Make = 0;
  sCtrl.u8Break = 0x40;
  sCtrl.pu8Address = &sg_pu8RecompBase[0x5040];
  ControlInstallAssignment(&sCtrl);
  sCtrl.pu8Address = sg_pu8RecompBase + 0x5040;
  ControlInstallAssignment(&sCtrl);

  // Install player controls

  //sCtrl.key = SDLK_UP;
  sCtrl.key = '\'';
  sCtrl.u8Mask = ~0x1;
  sCtrl.u8Make = 0;
  sCtrl.u8Break = 0x01;
  sCtrl.pu8Address = &sg_pu8RecompBase[0x5000];
  ControlInstallAssignment(&sCtrl);
  sCtrl.pu8Address = sg_pu8RecompBase + 0x5000;
  ControlInstallAssignment(&sCtrl);

  //sCtrl.key = SDLK_LEFT;
  sCtrl.key = 'z';
  sCtrl.u8Mask = ~0x2;
  sCtrl.u8Make = 0;
  sCtrl.u8Break = 0x02;
  sCtrl.pu8Address = &sg_pu8RecompBase[0x5000];
  ControlInstallAssignment(&sCtrl);
  sCtrl.pu8Address = sg_pu8RecompBase + 0x5000;
  ControlInstallAssignment(&sCtrl);

  //sCtrl.key = SDLK_RIGHT;
  sCtrl.key = 'x';
  sCtrl.u8Mask = ~0x4;
  sCtrl.u8Make = 0;
  sCtrl.u8Break = 0x04;
  sCtrl.pu8Address = &sg_pu8RecompBase[0x5000];
  ControlInstallAssignment(&sCtrl);
  sCtrl.pu8Address = sg_pu8RecompBase + 0x5000;
  ControlInstallAssignment(&sCtrl);

  // My portable's DOWN arrow key is almost unusable.  So I'm going to
  // change the SDL code to use snapper keys instead.
  //sCtrl.key = SDLK_DOWN;
  sCtrl.key = '/';
  sCtrl.u8Mask = ~0x8;
  sCtrl.u8Make = 0;
  sCtrl.u8Break = 0x08;
  sCtrl.pu8Address = &sg_pu8RecompBase[0x5000];
  ControlInstallAssignment(&sCtrl);
  sCtrl.pu8Address = sg_pu8RecompBase + 0x5000;
  ControlInstallAssignment(&sCtrl);

  // Install a way out
  sCtrl.key = SDLK_ESCAPE;

  sCtrl.u8Mask = 0;
  sCtrl.u8Make = 0x0;
  sCtrl.u8Break = 0x0;
  sCtrl.pu8Address = (UINT8 *)&sg_bMSPacmanRun;
  ControlInstallAssignment(&sCtrl);
#endif
  
  while (sg_bMSPacmanRun) {

    if (u32LastTime != time(0)) {
      // Don't output frame count directly to the console in case this
      // is an ncurses version of MSPac.  Instead pass it on to the
      // system-specific graphics interface which can choose where to
      // display it, or not display it at all.
#ifdef USE_NCURSES_GRAPHICS
      graphics_frames_taken(u32FrameCount, u32LastTime);
#endif
      // printf("%d Frames\n", u32FrameCount);
      u32FrameCount = 0;
      u32LastTime = time(0);
    }

    // The original timing scheme which relied on cycle counting will not work for a
    // translation that does not include code to count cycles.  We need to use an
    // alternative strategy, which is to return from the emulation whenever the
    // z80 code executes rst 38 in order to wait for vblank.  So far, experiments
    // in that area have not been successful.  (See rst 38 code in mspacman.c)
    
    u32FrameCount++;
    u32ThisCycles = TSTATES_PER_FRAME + u32CreditDebit;
    u32ExecResult = mspacman_exec(u32ThisCycles);

    if (u32ExecResult) {
      printf("Recomp bailed: Address %.4xh, unknown address to jump to (came from %.4xh)\n", u32ExecResult,
             u32RecompPriorPC);
      exit(1);
    }

    u32CreditDebit = (TSTATES_PER_FRAME - mspacman_GetElapsedTicks(TRUE));

    if (sg_bMSIntEnabled) {
      mspacman_int(sg_u8MSPacIntAddr);
    }

    if (Draw_Frame) { // currently 'draw frame' is set in mspacman.c on every rst 38
      MSPacmanDrawFrame(bDoBlit);
      Draw_Frame = 0;
    }
  }

  GfxShutdown();

#ifdef USE_NCURSES_GRAPHICS
  graphics_terminate();  // application graphics output
#endif

  return (0);
}
