]> Shamusworld >> Repos - stargem2/blobdiff - src/stargem2.cpp
Finally fixed problems with demo mode.
[stargem2] / src / stargem2.cpp
old mode 100755 (executable)
new mode 100644 (file)
index edad8fd..67fd98d
@@ -1,18 +1,20 @@
 //
 // Stargate Emulator (StarGem2) v2.0 SDL
 //
-// by James L. Hammons
-// (C) 2006 Underground Software
+// by James Hammons
+// (C) 2006, 2023 Underground Software
 //
-// JLH = James L. Hammons <jlhamm@acm.org>
+// JLH = James Hammons <jlhamm@acm.org>
 //
 // WHO  WHEN        WHAT
 // ---  ----------  ------------------------------------------------------------
 // JLH  06/15/2006  Added changelog ;-)
 // JLH  06/15/2006  Switched over to timeslice execution code
+// JLH  07/15/2009  Solved problem with DEMO mode (IRQ handling) (No, didn't)
+// JLH  01/03/2023  FINALLY solved problem with DEMO mode (v6809 timing)
 //
 
-#include "SDL.h"
+#include <SDL2/SDL.h>
 #include <fstream>
 #include <string>
 #include <iomanip>
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
-#include "types.h"
+#include <stdint.h>
+#include "dis6808.h"
+#include "dis6809.h"
 #include "log.h"
+#include "settings.h"
+#include "sound.h"
+#include "timing.h"
 #include "v6808.h"
 #include "v6809.h"
+#include "v6821.h"
 #include "video.h"
-#include "sound.h"
-#include "timing.h"
-#include "settings.h"
-#include "dis6809.h"
-#include "dis6808.h"
-
-#define __DEBUG__
 
 using namespace std;
 
@@ -39,218 +40,44 @@ using namespace std;
 #define CMOS           "cmos.ram"
 #define SAVESTATE      "sg2.state"
 
-// Global variables
-
-uint8 gram[0x10000], grom[0x10000], sram[0x10000], srom[0x10000]; // RAM & ROM spaces
-V6809REGS mainCPU;
-V6808REGS soundCPU;
-uint8 color[16];
-uint32 palette[256];
+#define FRAME_DURATION_IN_CYCLES               (M6809_CLOCK_SPEED_IN_HZ / 60.0)
+#define SCANLINE_DURATION_IN_CYCLES            (FRAME_DURATION_IN_CYCLES / 256.0)
+#define SG2_PIA_CALLBACK_DURATION              (SCANLINE_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC * 16.0)
 
-// Local variables
+// Function prototypes
 
-static bool running = true;                                            // Machine running state flag...
-static uint32 startTicks;
-static uint8 * keys;                                                   // SDL raw keyboard matrix
+uint8_t RdMem6809(uint16_t addr);
+void WrMem6809(uint16_t addr, uint8_t b);
+void Handle6809IRQ(bool);
+uint8_t RdMem6808(uint16_t addr);
+void WrMem6808(uint16_t addr, uint8_t b);
+bool LoadImg(const char * filename, uint8_t * ram, int size);
+void SaveCMOS(void);
+bool LoadMachineState(void);
+void SaveMachineState(void);
 
 // Local timer callback functions
 
 static void FrameCallback(void);
 static void ScanlineCallback(void);
 
+// Global variables
 
-//
-// 6809 memory functions
-//
-
-uint8 RdMem6809(uint16 addr)
-{
-       uint8 b;
-
-       if (addr >= 0x9000 && addr <= 0xCFFF)           // No ROM between $9000 - $CFFF...
-               b = gram[addr];
-       else
-       {
-               if (!gram[0xC900] && addr <= 0x8FFF)    // Check RAM $C900 bank switch
-                       b = gram[addr];
-               else
-                       b = grom[addr];
-       }
-
-//Is $C80E COUNT240? Hmm... No.
-
-//temp...
-/*extern uint16 pcr;
-//if (addr >= 0xC000 && addr <= 0xCBFF)
-if (addr == 0x9C59)
-       WriteLog("RdMem: Reading address %04X [=%02X, PC=%04X]\n", addr, b, pcr);//*/
-/*if (addr >= 0xC80D && addr <= 0xC80F)
-       WriteLog("V6809 RdMem: Reading address %04X [=%02X, PC=%04X]\n", addr, b, mainCPU.pc);//*/
-
-       return b;
-}
-
-void WrMem6809(uint16 addr, uint8 b)
-{
-//temp...
-//extern V6809REGS regs;
-//if (addr >= 0xC800 && addr <= 0xCBFE)
-//if (addr == 0xC80F || addr == 0xC80D)
-//     WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
-//if (addr == 0xC80E)
-/*if (addr >= 0xC800 && addr <= 0xC80F)
-       WriteLog("V6809 WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, mainCPU.pc, gram[0xCB00]);//*/
-
-       gram[addr] = b;
-
-       if (addr > 0x0006 && addr < 0x97F7)                     // 304 pixels  152-128=24-16=8
-       {
-               // NOTE: Screen was 304 x 256, but we truncate the vertical dimension here...
-               uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
-
-               if (sy > 5 && sy < 246)
-               {
-                       uint32 saddr = 8 + sx + ((sy - 6) * 320);       // Calc screen address
-//Hmm. This approach won't work with palette color cycling...
-#if 0
-                       scrBuffer[saddr + 0] = b >> 4;
-                       scrBuffer[saddr + 1] = b & 0x0F;
-#else
-                       scrBuffer[saddr + 0] = palette[color[b >> 4]];
-                       scrBuffer[saddr + 1] = palette[color[b & 0x0F]];
-#endif
-               }
-       }
-       else if (addr >= 0xC000 && addr <= 0xC00F)
-//Let's see if we can fix the color cycling here... [DONE]
-#if 0
-               color[addr - 0xC000] = b;                                               // color[] from VIDEO.CPP (not any more!)
-#else
-       {
-// A better strategy here would probably be to set a flag when the color register changes,
-// then change it before doing the render.
-// ALSO: This approach doesn't take the color to the edges of the screen
-               color[addr - 0xC000] = b;
-
-               for(uint32 addr=0x0007; addr<0x97F7; addr++)
-               {
-                       uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
-
-                       if (sy > 5 && sy < 246)
-                       {
-                               uint32 saddr = 8 + sx + ((sy - 6) * 320);       // Calc screen address
-                               uint8 sb = gram[addr];
-
-                               scrBuffer[saddr + 0] = palette[color[sb >> 4]];
-                               scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
-                       }
-               }
-       }
-#endif
-       else if (addr == 0xC80E)
-       {
-               sram[0x0402] = b;                                                               // Connect PIAs in 6809 & 6808
-               soundCPU.cpuFlags |= V6808_ASSERT_LINE_IRQ;             // Start sound IRQ
-       }
-}
-
-//
-// 6808 memory functions
-//
-
-uint8 RdMem6808(uint16 addr)
-{
-       return (addr < 0xF000 ? sram[addr] : srom[addr]);
-}
-
-void WrMem6808(uint16 addr, uint8 b)
-{
-       sram[addr] = b;
-
-       // A total guess, but let's try it...
-//It probably depends on how the PIA is configured, so this is most likely wrong.
-//     if (addr == 0x0401)
-//             soundCPU.cpuFlags &= ~V6808_ASSERT_LINE_IRQ;
-}
-
-//
-// Load a file into RAM/ROM image space
-//
-bool LoadImg(char * filename, uint8 * ram, int size)
-{
-       FILE * fp = fopen(filename, "rb");
-
-       if (fp == NULL)
-               return false;
-
-       fread(ram, 1, size, fp);
-       fclose(fp);
-
-       return true;
-}
-
-//
-// Save CMOS ram
-//
-void SaveCMOS(void)
-{
-       FILE * fp = fopen(CMOS, "wb");
-
-       if (fp != NULL)
-       {
-               fwrite(gram + 0xCC00, 1, 1024, fp);
-               fclose(fp);
-       }
-       else
-               WriteLog("CMOS RAM not saved!\n");
-}
-
-//
-// Load state save file
-//
-bool LoadMachineState(void)
-{
-       FILE * fp = fopen(SAVESTATE, "rb");
-
-       if (fp == NULL)
-               return false;
-
-       // This is kinda crappy--we don't do any sanity checking here!!!
-       fread(gram, 1, 0x10000, fp);
-       fread(sram, 1, 0x10000, fp);
-       fread(&mainCPU, 1, sizeof(V6809REGS), fp);
-       fread(&soundCPU, 1, sizeof(V6808REGS), fp);
-       fclose(fp);
-
-       for(int i=0x0006; i<0x97F8; i++)                                        // Set up backbuffer... ;-)
-               WrMem6809(i, gram[i]);
-
-       mainCPU.RdMem = RdMem6809;                                                      // Make sure our function pointers are
-       mainCPU.WrMem = WrMem6809;                                                      // pointing to the right places!
-       soundCPU.RdMem = RdMem6808;
-       soundCPU.WrMem = WrMem6808;
-
-       return true;
-}
+uint8_t gram[0x10000], grom[0x10000], sram[0x10000], srom[0x10000]; // RAM & ROM spaces
+V6809REGS mainCPU;
+V6808REGS soundCPU;
+V6821PIA pia1;
+V6821PIA pia2(Handle6809IRQ, Handle6809IRQ);
+uint8_t color[16];
+uint32_t palette[256];
+bool paletteDirty = false;
 
-//
-// Save state save file
-//
-void SaveMachineState(void)
-{
-       FILE * fp = fopen(SAVESTATE, "wb");
+// Local variables
 
-       if (fp != NULL)
-       {
-               fwrite(gram, 1, 0x10000, fp);
-               fwrite(sram, 1, 0x10000, fp);
-               fwrite(&mainCPU, 1, sizeof(V6809REGS), fp);
-               fwrite(&soundCPU, 1, sizeof(V6808REGS), fp);
-               fclose(fp);
-       }
-       else
-               WriteLog("Machine state not saved!\n");
-}
+static bool running = true;                    // Machine running state flag...
+static uint32_t startTicks;
+static const uint8_t * keys;           // SDL raw keyboard matrix
+uint64_t clockFrameStart;                      // V6809 clock at the start of the frame
 
 //
 // Main loop
@@ -258,12 +85,13 @@ void SaveMachineState(void)
 int main(int /*argc*/, char * /*argv*/[])
 {
        InitLog("stargem2.log");
-       WriteLog("StarGem2 - A portable Stargate emulator by James L. Hammons\n");
+       WriteLog("StarGem2 - A portable Stargate emulator by James Hammons\n");
+       WriteLog("(C) 2023 Underground Software\n\n");
 
        LoadSettings();
 
        // Initialize Williams' palette (RGB coded as: 3 bits red, 3 bits green, 2 bits blue)
-       for(uint32 i=0; i<256; i++)
+       for(uint32_t i=0; i<256; i++)
                palette[i] =
 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
                (((i & 0x01) * 33 + ((i & 0x02) >> 1) * 71 + ((i & 0x04) >> 2) * 151) << 24)
@@ -276,37 +104,35 @@ int main(int /*argc*/, char * /*argv*/[])
 #endif
 
        // Zero out memory
-       for(long i=0; i<0x10000; i++)
-               gram[i] = grom[i] = sram[i] = srom[i] = 0;
+       memset(gram, 0, 0x10000);
+       memset(grom, 0, 0x10000);
+       memset(sram, 0, 0x10000);
+       memset(srom, 0, 0x10000);
 
        // Set up V6809 & V6808 execution contexts
-       
-       memset(&mainCPU, sizeof(V6809REGS), 0);
+
+       memset(&mainCPU, 0, sizeof(V6809REGS));
        mainCPU.RdMem = RdMem6809;
        mainCPU.WrMem = WrMem6809;
-       mainCPU.cpuFlags |= V6809_ASSERT_LINE_RESET;
+       mainCPU.cpuFlags |= V6809_LINE_RESET;
 
-       memset(&soundCPU, sizeof(V6808REGS), 0);
+       memset(&soundCPU, 0, sizeof(V6808REGS));
        soundCPU.RdMem = RdMem6808;
        soundCPU.WrMem = WrMem6808;
-       soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
+       soundCPU.cpuFlags |= V6808_LINE_RESET;
 
        char ROMs[12][8] = {
                "ROMs/01", "ROMs/02", "ROMs/03", "ROMs/04", "ROMs/05", "ROMs/06",
                "ROMs/07", "ROMs/08", "ROMs/09", "ROMs/10", "ROMs/11", "ROMs/12"
-               };
+       };
 
        for(int i=0; i<12; i++)
        {
-               uint32 baseAddress = i * 0x1000;
+               uint32_t baseAddress = i * 0x1000;
 
                if (i > 8)
                        baseAddress += 0x4000;
 
-#if 0
-WriteLog("Loading ROM image '%s' at $%04X...\n", ROMs[i], baseAddress);
-#endif
-//             if (!LoadImg(ROMs[i], grom + (i * 0x1000), 0x1000))
                if (!LoadImg(ROMs[i], grom + baseAddress, 0x1000))
                {
                        WriteLog("Could not open file '%s'!\n", ROMs[i]);
@@ -342,177 +168,14 @@ WriteLog("Loading ROM image '%s' at $%04X...\n", ROMs[i], baseAddress);
 
        WriteLog("About to intialize audio...\n");
        SoundInit();
-//     uint8 * keys = SDL_GetKeyState(NULL);
-       keys = SDL_GetKeyState(NULL);
-
-//     running = true;                                                         // Set running status...
-       srom[0xF800] = 0x37;                                            // Fix checksum so ST works...
-
-#if 0
-
-//kludge...
-//This didn't work--it still acted like the old way (interrupt @ VC = 0)
-//gram[0xCB00] = 64*3;
-
-       WriteLog("Entering main loop...\n");
-       while (running)
-       {
-               SDL_PumpEvents();                                               // Force key events into the buffer.
-               gram[0xC804] = gram[0xC806] = gram[0xC80C] = 0; // Reset PIA ports...
-
-               if (keys[SDLK_ESCAPE])
-                       running = false;                                        // ESC to exit...
-
-               if (keys[SDLK_SEMICOLON])
-                       gram[0xC804] |= 0x01;                           // Fire (;)
-               if (keys[SDLK_l])
-                       gram[0xC804] |= 0x02;                           // Thrust (L)
-               if (keys[SDLK_SPACE])
-                       gram[0xC804] |= 0x04;                           // Smart Bomb (space)
-               if (keys[SDLK_BACKSPACE])
-                       gram[0xC804] |= 0x08;                           // Hyperspace (BkSp)
-               if (keys[SDLK_2])
-                       gram[0xC804] |= 0x10;                           // Two Player Start (2)
-               if (keys[SDLK_1])
-                       gram[0xC804] |= 0x20;                           // One Player Start (1)
-               if (keys[SDLK_RETURN])
-                       gram[0xC804] |= 0x40;                           // Reverse (Enter)
-               if (keys[SDLK_f])
-                       gram[0xC804] |= 0x80;                           // Down (F)
-
-               if (keys[SDLK_r])
-                       gram[0xC806] |= 0x01;                           // Up (R)
-               if (keys[SDLK_a])
-                       gram[0xC806] |= 0x02;                           // Inviso (A)
-
-               if (keys[SDLK_F1])
-                       gram[0xC80C] |= 0x01;                           // Auto up (F1)
-               if (keys[SDLK_F2])
-                       gram[0xC80C] |= 0x02;                           // Advance (F2)
-               if (keys[SDLK_5])
-                       gram[0xC80C] |= 0x04;                           // Right Coin (5)
-               if (keys[SDLK_F3])
-                       gram[0xC80C] |= 0x08;                           // High Score Reset (F3)
-               if (keys[SDLK_3])
-                       gram[0xC80C] |= 0x10;                           // Left Coin (3)
-               if (keys[SDLK_4])
-                       gram[0xC80C] |= 0x20;                           // Center Coin (4)
-               if (keys[SDLK_F4])
-                       gram[0xC80C] |= 0x40;                           // Slam Switch (F4)
-
-               if (keys[SDLK_F5])                                              // Sound CPU self-test (F5)
-                       soundCPU.cpuFlags |= V6808_ASSERT_LINE_NMI;
-               if (keys[SDLK_F6])                                              // Reset the 6808 (F6)
-                       soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
-
-/*
-$CB00 is scanline counter, bits 2-7 (1 frame/240 =69.44... usec)
-
-Some places of interest to look at:
-
-RdMem: Reading address C80E [=0C, PC=15C3]     <- Inside interrupt (read, then discarded)...
-RdMem: Reading address CB00 [=43, PC=15C6]     <- interrupt
-RdMem: Reading address C80C [=00, PC=0758]     <- input (?)
-RdMem: Reading address C80C [=00, PC=07B9]     <- input (?)
-RdMem: Reading address C806 [=00, PC=078C]     <- input
-RdMem: Reading address C804 [=00, PC=2679]     <- input
-*/
-               uint32 startTicks = SDL_GetTicks();
-//             long video_clk = 0;
-//             gram[0xCB00] = 0;
-
-/*
-//This is where the interrupt mask is restored in CC... Hmm...
-//This enables interrupts *after* the previous interrupt has occurred... Hmm.
-//Could $C80F (rom_pia_ctrlb) be the IRQ inhibit? Yes, it is!
-
-       // the IRQ signal comes into CB1, and is set to VA11
-       pia_1_cb1_w(0, scanline & 0x20);
-...
-       // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
-       pia_1_ca1_w(0, 0);
-...
-       // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
-       pia_1_ca1_w(0, 1);
-*/
-
-//WriteLog("--> Start of frame...\n");
-               for(int i=0; i<3; i++)
-               {
-//Not sure, but this *might* fix IRQ problem...
-//Checking the PIA IRQ mask for an IRQ seems to work OK. Now if only the timing elsewhere was right...
-                       if (gram[0xC80F] & 0x01)
-                               mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
-
-                       Execute6809(&mainCPU, 4000);
-                       mainCPU.clock -= 4000;                          // Remove 4K ticks from clock (in case it overflowed)
-//Not sure, but this *might* fix IRQ problem...
-//Checking the PIA IRQ mask for an IRQ seems to work OK. Now if only the timing elsewhere was right...
-/*                     if (gram[0xC80F] & 0x01)
-                               mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
-
-                       gram[0xCB00] += 64;                                     // Update video counter...
-               }
-
-//Hmm.
-/*if (gram[0xC80E] & 0x01)
-       mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
-//48 lines... (+ 16)
-//gram[0xCB00] = 0;
-               Execute6809(&mainCPU, 3000);
-               mainCPU.clock -= 3000;                                  // Remove 3K ticks from clock (in case it overflowed)
-//Not sure, but this *might* fix IRQ problem...
-//if (gram[0xC80F] & 0x01)
-//This isn't the right port on the PIA, but it does seem to make it through the demo now...
-//Lesse if this works... Seems to!
-               if (gram[0xC80D] & 0x01)                                // Do COUNT240 IRQ (if enabled!)
-                       mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
-
-/*if (gram[0xC80F] & 0x01)
-               mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
-gram[0xCB00] = 0; //*/
-
-               gram[0xCB00] += 48;                                             // Update video counter...
-
-               Execute6809(&mainCPU, 1000);
-               mainCPU.clock -= 1000;                                  // Remove 1K ticks from clock (in case it overflowed)
-//Eh?
-//Ok, this is the interrupt it's looking for, but still...
-//if (gram[0xC80F] & 0x01)
-//             mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
-
-               gram[0xCB00] += 16;                                             // Update video counter...
-
-//             RenderScreenBuffer();
-               RenderScreenBuffer2();  // 1 frame = 16667 cycles
-//             WriteLog("Main: Rendered back buffer. [6809 PC=%04X]\n", pcr);
-
-               Execute6809(&mainCPU, 667);                             // Do QnD VBLANK
-
-               // 1/60 sec = ? ms (16.6 ms)
-               while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
-
-//kludge, temp...
-//Very interesting! It's the palette rotation that's slowing it down!
-//Fixed now, but this allows the color rotation while the wrong timing is in effect...
-/*for(int i=0; i<16; i++)
-       WrMem(0xC000 + i, gram[0x9C26 + i]);//*/
-       }
-
-/*uint16 pc = 0x15BA;
-for(int i=0; i<200; i++)
-//while (pc < 0x9000)
-       pc += Decode6809(pc);//*/
-
-#else
-
-       running = true;                                                         // Set running status...
-
-       InitializeEventList();                                          // Clear the event list before we use it...
-       SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
-//     SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 s intervals
-       SetCallbackTime(ScanlineCallback, 520.83333334); // Set scanline callback at 1/32 of frame
-//     SetCallbackTime(ScanlineCallback, 520.83333334*32.00); // Set scanline callback at 1/32 of frame
+       keys = SDL_GetKeyboardState(NULL);
+       srom[0xF800] = 0x37;                    // Fix checksum so Self-Test works...
+       running = true;                                 // Set running status...
+
+       InitializeEventList();                  // Clear the event list before we use it...
+       SetCallbackTime(FrameCallback, FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC);
+       SetCallbackTime(ScanlineCallback, SG2_PIA_CALLBACK_DURATION);
+       clockFrameStart = mainCPU.clock;
        startTicks = SDL_GetTicks();
 
        WriteLog("Entering main loop...\n");
@@ -521,36 +184,9 @@ for(int i=0; i<200; i++)
        {
                double timeToNextEvent = GetTimeToNextEvent();
                Execute6809(&mainCPU, USEC_TO_M6809_CYCLES(timeToNextEvent));
-//We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
-//(Fix so that this is not a requirement!)
-               mainCPU.clock -= USEC_TO_M6809_CYCLES(timeToNextEvent);
                HandleNextEvent();
        }
 
-#ifdef __DEBUG__
-WriteLog("\n");
-WriteLog("$C900 = $%02X (0=RAM)\n", gram[0xC900]);
-WriteLog("PC: %04X, X: %04X, Y: %04X, S: %04X, U: %04X, A: %02X, B: %02X, DP: %02X, CC: %02X\n", mainCPU.pc, mainCPU.x, mainCPU.y, mainCPU.s, mainCPU.u, mainCPU.a, mainCPU.b, mainCPU.dp, mainCPU.cc);
-WriteLog("\n");
-
-/*uint16 pc = mainCPU.pc;//0x15BA;
-for(int i=0; i<200; i++)
-//while (pc < 0x9000)
-{
-       pc += Decode6809(pc);
-       WriteLog("\n");
-}//*/
-
-/*uint32 pc = 0;
-while (pc < 0xFFFF)
-{
-       pc += Decode6809(pc);
-       WriteLog("\n");
-}//*/
-#endif
-
-#endif
-
        SoundDone();
        VideoDone();
        SaveCMOS();
@@ -560,468 +196,303 @@ while (pc < 0xFFFF)
        return 0;
 }
 
-static void FrameCallback(void)
+//
+// Load a file into RAM/ROM image space
+//
+bool LoadImg(const char * filename, uint8_t * ram, int size)
 {
-       SDL_PumpEvents();                                                       // Force key events into the buffer.
-       gram[0xC804] = gram[0xC806] = gram[0xC80C] = 0; // Reset PIA ports...
-
-       if (keys[SDLK_ESCAPE])
-               running = false;                                                // ESC to exit...
-
-//Convert this stuff to use the settings module... !!! FIX !!!
-       if (keys[SDLK_SEMICOLON])
-               gram[0xC804] |= 0x01;                                   // Fire (;)
-       if (keys[SDLK_l])
-               gram[0xC804] |= 0x02;                                   // Thrust (L)
-       if (keys[SDLK_SPACE])
-               gram[0xC804] |= 0x04;                                   // Smart Bomb (space)
-       if (keys[SDLK_BACKSPACE])
-               gram[0xC804] |= 0x08;                                   // Hyperspace (BkSp)
-       if (keys[SDLK_2])
-               gram[0xC804] |= 0x10;                                   // Two Player Start (2)
-       if (keys[SDLK_1])
-               gram[0xC804] |= 0x20;                                   // One Player Start (1)
-       if (keys[SDLK_RETURN])
-               gram[0xC804] |= 0x40;                                   // Reverse (Enter)
-       if (keys[SDLK_f])
-               gram[0xC804] |= 0x80;                                   // Down (F)
-
-       if (keys[SDLK_r])
-               gram[0xC806] |= 0x01;                                   // Up (R)
-       if (keys[SDLK_a])
-               gram[0xC806] |= 0x02;                                   // Inviso (A)
-
-       if (keys[SDLK_F1])
-               gram[0xC80C] |= 0x01;                                   // Auto up (F1)
-       if (keys[SDLK_F2])
-               gram[0xC80C] |= 0x02;                                   // Advance (F2)
-       if (keys[SDLK_5])
-               gram[0xC80C] |= 0x04;                                   // Right Coin (5)
-       if (keys[SDLK_F3])
-               gram[0xC80C] |= 0x08;                                   // High Score Reset (F3)
-       if (keys[SDLK_3])
-               gram[0xC80C] |= 0x10;                                   // Left Coin (3)
-       if (keys[SDLK_4])
-               gram[0xC80C] |= 0x20;                                   // Center Coin (4)
-       if (keys[SDLK_F4])
-               gram[0xC80C] |= 0x40;                                   // Slam Switch (F4)
-
-       if (keys[SDLK_F5])                                                      // Sound CPU self-test (F5)
-               soundCPU.cpuFlags |= V6808_ASSERT_LINE_NMI;
-       if (keys[SDLK_F6])                                                      // Reset the 6808 (F6)
-               soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
-
-       RenderScreenBuffer2();                                          // 1 frame = 1/60 sec ~ 16667 cycles
-       SetCallbackTime(FrameCallback, 16666.66666667);
-
-       while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
-       startTicks = SDL_GetTicks();
-}
+       FILE * fp = fopen(filename, "rb");
 
-static void ScanlineCallback(void)
-{
-// CA1 of PIA 1 maps to $C80C-F... <-- Count240 is in PIA1...
-// What about COUNT240???
-
-//wil wok? Yes, but still screws up on the demo...
-/*     if (gram[0xCB00] & 0x20)
-               if (gram[0xC80F] & 0x01)
-                       mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
-       if ((gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
-               mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
-
-//Is $C80E COUNT240? Hmm... Doesn't seem to be. Bleh.
-/*     if (gram[0xCB00] >= 240)
-               gram[0xC80E] = 0xFF;
-       else
-               gram[0xC80E] = 0x00;//*/
-//     gram[0xC80E] = (gram[0xCB00] >= 240 ? 0xFF : 0x00);
+       if (fp == NULL)
+               return false;
 
-       gram[0xCB00] += 8;                                                      // Update video counter...
+       size_t ignoredResult = fread(ram, 1, size, fp);
+       fclose(fp);
 
-       SetCallbackTime(ScanlineCallback, 520.83333334); // Set scanline callback at 1/32 of frame
+       return true;
 }
 
-
-/*
-; With correct timing, but no color cycling
-
---> Start of frame...
-WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
-At $07AD. $6E: 00
-At $0B66. $6E: 00
-At $0CF4. $6E: 00
-At $0CCB. $6E: 00
-WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
-WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
-At $0718. $6E: 01
-At $07AD. $6E: 01
-At $0BB8. $6E: 01
-At $0927. $6E: 01
-At $0CF4. $6E: 01
-At $0B66. $6E: 01
-At $16C8. $6E: 01
-WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
-WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
-WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
-
-
-; With incorrect timing, but has color cycling
-
---> Start of frame...
-WrMem: Writing address C80F with 34 [PC=15C3, $CB00=00]
-At $1609. $6E: 00                      ; Color cycling...
-At $07AD. $6E: 00
-At $0B66. $6E: 00
-At $0CF4. $6E: 00
-At $0CCB. $6E: 00
-WrMem: Writing address C80F with 35 [PC=1644, $CB00=00]
-WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
-WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
-WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
-At $0718. $6E: 01
-At $07AD. $6E: 01
-At $0BB8. $6E: 01
-At $0927. $6E: 01
-At $0CF4. $6E: 01
-At $0B66. $6E: 01
-At $16C8. $6E: 01
-WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
-WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
-WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
-
-
-
-       Stargate
-       --------
-
-       0000-8FFF ROM   (for Blaster, 0000-3FFF is a bank of 12 ROMs)
-       0000-97FF Video  RAM Bank switched with ROM (96FF for Blaster)
-       9800-BFFF RAM
-               0xBB00 Blaster only, Color 0 for each line (256 entry)
-               0xBC00 Blaster only, Color 0 flags, latch color only if bit 0 = 1 (256 entry)
-                   Do something else with the bit 1, I do not know what
-       C000-CFFF I/O
-       D000-FFFF ROM
-
-       C000-C00F color_registers  (16 bytes of BBGGGRRR)
-
-       C804 widget_pia_dataa (widget = I/O board)
-       C805 widget_pia_ctrla
-       C806 widget_pia_datab
-       C807 widget_pia_ctrlb (CB2 select between player 1 and player 2
-                              controls if Table or Joust)
-             bits 5-3 = 110 = player 2
-             bits 5-3 = 111 = player 1
-
-       C80C rom_pia_dataa
-       C80D rom_pia_ctrla
-       C80E rom_pia_datab
-             bit 0 \
-             bit 1 |
-             bit 2 |-6 bits to sound board
-             bit 3 |
-             bit 4 |
-             bit 5 /
-             bit 6 \
-             bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
-       C80F rom_pia_ctrlb
-
-       C900 rom_enable_scr_ctrl  Switch between video ram and rom at 0000-97FF
-
-       Stargate
-       --------
-       C804 widget_pia_dataa (widget = I/O board)
-         bit 0  Fire
-         bit 1  Thrust
-         bit 2  Smart Bomb
-         bit 3  HyperSpace
-         bit 4  2 Players
-         bit 5  1 Player
-         bit 6  Reverse
-         bit 7  Down
-
-       C806 widget_pia_datab
-         bit 0  Up
-         bit 1  Inviso
-         bit 2
-         bit 3
-         bit 4
-         bit 5
-         bit 6
-         bit 7  0 = Upright  1 = Table
-
-       C80C rom_pia_dataa
-         bit 0  Auto Up
-         bit 1  Advance
-         bit 2  Right Coin        (High Score Reset in schematics)
-         bit 3  High Score Reset  (Left Coin in schematics)
-         bit 4  Left Coin         (Center Coin in schematics)
-         bit 5  Center Coin       (Right Coin in schematics)
-         bit 6  Slam Door Tilt
-         bit 7  Hand Shake from sound board
-*/
-
-
-/*
-
-static MEMORY_READ_START( williams_readmem )
-       { 0x0000, 0x97ff, MRA_BANK1 },
-       { 0x9800, 0xbfff, MRA_RAM },
-       { 0xc804, 0xc807, pia_0_r },
-       { 0xc80c, 0xc80f, pia_1_r },
-       { 0xcb00, 0xcb00, williams_video_counter_r },
-       { 0xcc00, 0xcfff, MRA_RAM },
-       { 0xd000, 0xffff, MRA_ROM },
-MEMORY_END
-
-
-static MEMORY_WRITE_START( williams_writemem )
-       { 0x0000, 0x97ff, williams_videoram_w, &williams_bank_base, &videoram_size },
-       { 0x9800, 0xbfff, MWA_RAM },
-       { 0xc000, 0xc00f, paletteram_BBGGGRRR_w, &paletteram },
-       { 0xc804, 0xc807, pia_0_w },
-       { 0xc80c, 0xc80f, pia_1_w },
-       { 0xc900, 0xc900, williams_vram_select_w },
-       { 0xca00, 0xca07, williams_blitter_w, &williams_blitterram },
-       { 0xcbff, 0xcbff, watchdog_reset_w },
-       { 0xcc00, 0xcfff, MWA_RAM },
-       { 0xd000, 0xffff, MWA_ROM },
-MEMORY_END
-
-static MEMORY_READ_START( sound_readmem )
-       { 0x0000, 0x007f, MRA_RAM },
-       { 0x0400, 0x0403, pia_2_r },
-       { 0x8400, 0x8403, pia_2_r },    // used by Colony 7, perhaps others?
-       { 0xb000, 0xffff, MRA_ROM },    // most games start at $F000, Sinistar starts at $B000
-MEMORY_END
-
-
-static MEMORY_WRITE_START( sound_writemem )
-       { 0x0000, 0x007f, MWA_RAM },
-       { 0x0400, 0x0403, pia_2_w },
-       { 0x8400, 0x8403, pia_2_w },    // used by Colony 7, perhaps others?
-       { 0xb000, 0xffff, MWA_ROM },    // most games start at $F000, Sinistar starts at $B000
-MEMORY_END
-
-MACHINE_INIT( williams )
+//
+// Save CMOS ram
+//
+void SaveCMOS(void)
 {
-       // reset the PIAs
-       pia_reset();
-
-       // reset the ticket dispenser (Lotto Fun)
-       ticket_dispenser_init(70, TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_HIGH);
+       FILE * fp = fopen(CMOS, "wb");
 
-       // set a timer to go off every 16 scanlines, to toggle the VA11 line and update the screen
-       timer_set(cpu_getscanlinetime(0), 0, williams_va11_callback);
+       if (fp == NULL)
+       {
+               WriteLog("CMOS RAM not saved!\n");
+               return;
+       }
 
-       // also set a timer to go off on scanline 240
-       timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
+       size_t ignoredResult = fwrite(gram + 0xCC00, 1, 1024, fp);
+       fclose(fp);
 }
 
-
-static void williams_va11_callback(int scanline)
+//
+// Load state save file
+//
+bool LoadMachineState(void)
 {
-       // the IRQ signal comes into CB1, and is set to VA11
-       pia_1_cb1_w(0, scanline & 0x20);
-
-       // update the screen while we're here
-       force_partial_update(scanline - 1);
+       FILE * fp = fopen(SAVESTATE, "rb");
 
-       // set a timer for the next update
-       scanline += 8;
-       if (scanline >= 256) scanline = 0;
-       timer_set(cpu_getscanlinetime(scanline), scanline, williams_va11_callback);
-}
+       if (fp == NULL)
+               return false;
 
+       // This is kinda crappy--we don't do any sanity checking here!!!
+       size_t ignored = fread(gram, 1, 0x10000, fp);
+       ignored = fread(sram, 1, 0x10000, fp);
+       ignored = fread(&mainCPU, 1, sizeof(V6809REGS), fp);
+       ignored = fread(&soundCPU, 1, sizeof(V6808REGS), fp);
+       ignored = fread(&pia1, 1, sizeof(V6821PIA), fp);
+       ignored = fread(&pia2, 1, sizeof(V6821PIA), fp);
+       fclose(fp);
 
-static void williams_count240_off_callback(int param)
-{
-       // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
-       pia_1_ca1_w(0, 0);
-}
+       // Set up backbuffer...  ;-)
+       for(uint16_t i=0x0006; i<0x97F8; i++)   // Screen memory
+               WrMem6809(i, gram[i]);
 
+       for(uint16_t i=0xC000; i<=0xC00F; i++)  // Palette memory
+               WrMem6809(i, gram[i]);
 
-static void williams_count240_callback(int param)
-{
-       // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
-       pia_1_ca1_w(0, 1);
+       paletteDirty = true;
 
-       // set a timer to turn it off once the scanline counter resets
-       timer_set(cpu_getscanlinetime(0), 0, williams_count240_off_callback);
+       mainCPU.RdMem = RdMem6809;                              // Make sure our function pointers are
+       mainCPU.WrMem = WrMem6809;                              // pointing to the right places!
+       soundCPU.RdMem = RdMem6808;
+       soundCPU.WrMem = WrMem6808;
+       mainCPU.clock = 0;                                              // Zero out our clocks...
+       soundCPU.clock = 0;
+       mainCPU.clockOverrun = 0;                               // And overrun values...
+//notyet       soundCPU.clockOverrun = 0;
+       pia1.IRQA = NULL;
+       pia1.IRQB = NULL;
+       pia2.IRQA = Handle6809IRQ;
+       pia2.IRQB = Handle6809IRQ;
 
-       // set a timer for next frame
-       timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
+       return true;
 }
 
-
-static void williams_main_irq(int state)
+//
+// Save state save file
+//
+void SaveMachineState(void)
 {
-       // IRQ to the main CPU
-       cpu_set_irq_line(0, M6809_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
-}
+       FILE * fp = fopen(SAVESTATE, "wb");
 
+       if (fp == NULL)
+       {
+               WriteLog("Machine state not saved!\n");
+               return;
+       }
 
-static void williams_main_firq(int state)
-{
-       // FIRQ to the main CPU
-       cpu_set_irq_line(0, M6809_FIRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
+       size_t ignored = fwrite(gram, 1, 0x10000, fp);
+       ignored = fwrite(sram, 1, 0x10000, fp);
+       ignored = fwrite(&mainCPU, 1, sizeof(V6809REGS), fp);
+       ignored = fwrite(&soundCPU, 1, sizeof(V6808REGS), fp);
+       ignored = fwrite(&pia1, 1, sizeof(V6821PIA), fp);
+       ignored = fwrite(&pia2, 1, sizeof(V6821PIA), fp);
+       fclose(fp);
 }
 
-
-static void williams_snd_irq(int state)
+//
+// 6809 memory functions
+//
+uint8_t RdMem6809(uint16_t addr)
 {
-       // IRQ to the sound CPU
-       cpu_set_irq_line(1, M6800_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
-}
+       uint8_t b;
 
+       if (addr >= 0x9000 && addr <= 0xCFFF)           // No ROM between $9000 - $CFFF...
+               b = gram[addr];
+       else
+       {
+               if (!gram[0xC900] && addr <= 0x8FFF)    // Check RAM $C900 bank switch
+                       b = gram[addr];
+               else
+                       b = grom[addr];
+       }
 
-READ_HANDLER( williams_video_counter_r )
-{
-       return cpu_getscanline() & 0xFC;
-}
+       if ((addr >= 0xC804) && (addr <= 0xC807))
+               b = pia1.Read(addr);
+       else if ((addr >= 0xC80C) && (addr <= 0xC80F))
+               b = pia2.Read(addr);
+       // A wee kludge (though I doubt it reads from anywhere other than $CB00)...
+       else if ((addr & 0xFF00) == 0xCB00)
+       {
+               double elapsedCycles = (double)(GetCurrentV6809Clock() - clockFrameStart);
+               uint32_t scanline = (uint32_t)(elapsedCycles / SCANLINE_DURATION_IN_CYCLES);
 
+               b = (uint8_t)scanline & 0xFC;                   // Only bits 2-7 are connected...
+       }
 
-// Special PIA 0 for Stargate, to handle the controls
-struct pia6821_interface stargate_pia_0_intf =
-{
-       //inputs : A/B,CA/B1,CA/B2 / stargate_input_port_0_r, input_port_1_r, 0, 0, 0, 0,
-       //outputs: A/B,CA/B2       / 0, 0, 0, 0,
-       //irqs   : A/B             / 0, 0
-};
+       return b;
+}
 
-// Generic PIA 1, maps to input port 2, sound command out, and IRQs
-struct pia6821_interface williams_pia_1_intf =
+void WrMem6809(uint16_t addr, uint8_t b)
 {
-       //inputs : A/B,CA/B1,CA/B2 / input_port_2_r, 0, 0, 0, 0, 0,
-       //outputs: A/B,CA/B2       / 0, williams_snd_cmd_w, 0, 0,
-       //irqs   : A/B             / williams_main_irq, williams_main_irq
-};
+       gram[addr] = b;
 
-// Generic PIA 2, maps to DAC data in and sound IRQs
-struct pia6821_interface williams_snd_pia_intf =
-{
-       //inputs : A/B,CA/B1,CA/B2 / 0, 0, 0, 0, 0, 0,
-       //outputs: A/B,CA/B2       / DAC_0_data_w, 0, 0, 0,
-       //irqs   : A/B             / williams_snd_irq, williams_snd_irq
-};
+       if (addr >= 0x0006 && addr <= 0x97F6)           // 304 pixels  152-128=24-16=8
+       {
+               // NOTE: Screen was 304 x 256, but we truncate the vertical dimension here...
+               uint16_t sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
 
-static DRIVER_INIT( stargate )
-{
-       // CMOS configuration
-       CONFIGURE_CMOS(0xCC00, 0x400);
+               if (sy >= 6 && sy <= 245)
+               {
+                       uint32_t saddr = 8 + sx + ((sy - 6) * 320);     // Calc screen address
+                       scrBuffer[saddr + 0] = palette[color[b >> 4]];
+                       scrBuffer[saddr + 1] = palette[color[b & 0x0F]];
+               }
+       }
+       else if (addr >= 0xC000 && addr <= 0xC00F)
+       {
+               // This approach doesn't take the BG color to the edges of the screen
+               color[addr & 0x0F] = b;
+               paletteDirty = true;
+       }
+       else if (addr == 0xC80E)
+       {
+               // Only write if not writing the DDR...
+               if ((pia2.crb & 0x04) != 0)
+               {
+                       sram[0x0402] = b;                                               // Connect PIAs in 6809 & 6808
+                       soundCPU.cpuFlags |= V6808_LINE_IRQ;    // Start sound IRQ
+               }
+       }
 
-       // PIA configuration
-       CONFIGURE_PIAS(stargate_pia_0_intf, williams_pia_1_intf, williams_snd_pia_intf);
+       if ((addr >= 0xC804) && (addr <= 0xC807))
+               pia1.Write(addr, b);
+       else if ((addr >= 0xC80C) && (addr <= 0xC80F))
+               pia2.Write(addr, b);
 }
 
-
-int cpu_getscanline(void)
+void Handle6809IRQ(bool state)
 {
-       return (int)(timer_timeelapsed(refresh_timer) * scanline_period_inv);
+       // N.B.: The IRQ line is active LOW
+       if (state)
+       {
+               // We do both, because we don't know if we're in an execution context or not...  :-P
+               SetLineOfCurrentV6809(V6809_LINE_IRQ);
+               mainCPU.cpuFlags |= V6809_LINE_IRQ;
+       }
+       else
+       {
+               // We do both, because we don't know if we're in an execution context or not...  :-P
+               ClearLineOfCurrentV6809(V6809_LINE_IRQ);
+               mainCPU.cpuFlags &= ~V6809_LINE_IRQ;
+       }
 }
 
- *************************************
- *
- *     Returns time until given scanline
- *
- *************************************
-
-double cpu_getscanlinetime(int scanline)
+//
+// 6808 memory functions
+//
+uint8_t RdMem6808(uint16_t addr)
 {
-       double scantime = timer_starttime(refresh_timer) + (double)scanline * scanline_period;
-       double abstime = timer_get_time();
-       double result;
-
-       // if we're already past the computed time, count it for the next frame
-       if (abstime >= scantime)
-               scantime += TIME_IN_HZ(Machine->drv->frames_per_second);
-
-       // compute how long from now until that time
-       result = scantime - abstime;
-
-       // if it's small, just count a whole frame
-       if (result < TIME_IN_NSEC(1))
-               result = TIME_IN_HZ(Machine->drv->frames_per_second);
-       return result;
+       return (addr < 0xF000 ? sram[addr] : srom[addr]);
 }
 
- *************************************
- *
- *     Returns time for one scanline
- *
- *************************************
-
-double cpu_getscanlineperiod(void)
+void WrMem6808(uint16_t addr, uint8_t b)
 {
-       return scanline_period;
+       sram[addr] = b;
 }
 
+//
+// Stargate frame callback
+//
+static void FrameCallback(void)
+{
+       SDL_PumpEvents();                                                       // Force key events into the buffer.
+       pia1.pa = pia1.pb = pia2.pa = 0;                        // Clear inputs...
+//don't do nuthin'
+//     gram[0xC80C] = 0x80;//temp, for testing (Hand Shake from sound board)
 
-V6809 WrMem: Writing address C80D with 00 [PC=0000, $CB00=00]
-V6809 WrMem: Writing address C80C with 00 [PC=0000, $CB00=00]
-V6809 WrMem: Writing address C80D with 3C [PC=0000, $CB00=00]
-
-V6809 WrMem: Writing address C80F with 00 [PC=0000, $CB00=00]
-V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
-V6809 WrMem: Writing address C80F with 3C [PC=0000, $CB00=00]
-
-V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
-V6809 WrMem: Writing address C80D with 34 [PC=FE61, $CB00=48]
-V6809 WrMem: Writing address C80F with 34 [PC=FE61, $CB00=48]
-V6809 WrMem: Writing address C80E with 00 [PC=FE61, $CB00=48]
-
-V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
-V6809 WrMem: Writing address C80D with 00 [PC=FD92, $CB00=C8]
-V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
-V6809 WrMem: Writing address C80D with 34 [PC=FD92, $CB00=C8]
-
-V6809 WrMem: Writing address C80E with 00 [PC=FD92, $CB00=C8]
-V6809 WrMem: Writing address C80F with 00 [PC=FD92, $CB00=C8]
-V6809 WrMem: Writing address C80E with FF [PC=FD92, $CB00=C8]
-V6809 WrMem: Writing address C80F with 35 [PC=FD92, $CB00=C8]
-
-V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
-V6809 WrMem: Writing address C805 with 00 [PC=607B, $CB00=D0]
-V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
-V6809 WrMem: Writing address C805 with 34 [PC=607B, $CB00=D0]
+       if (keys[SDL_SCANCODE_ESCAPE])
+               running = false;                                                // ESC to exit...
 
-V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
-V6809 WrMem: Writing address C807 with 00 [PC=607B, $CB00=D0]
-V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
-V6809 WrMem: Writing address C807 with 3E [PC=607B, $CB00=D0]
+       if (keys[settings.keyBindings[S_KEY_FIRE]])                     pia1.pa |= 0x01;
+       if (keys[settings.keyBindings[S_KEY_THRUST]])           pia1.pa |= 0x02;
+       if (keys[settings.keyBindings[S_KEY_SMARTBOMB]])        pia1.pa |= 0x04;
+       if (keys[settings.keyBindings[S_KEY_HYPERSPACE]])       pia1.pa |= 0x08;
+       if (keys[settings.keyBindings[S_KEY_2P_START]])         pia1.pa |= 0x10;
+       if (keys[settings.keyBindings[S_KEY_1P_START]])         pia1.pa |= 0x20;
+       if (keys[settings.keyBindings[S_KEY_REVERSE]])          pia1.pa |= 0x40;
+       if (keys[settings.keyBindings[S_KEY_DOWN]])                     pia1.pa |= 0x80;
+
+       if (keys[settings.keyBindings[S_KEY_UP]])                       pia1.pb |= 0x01;
+       if (keys[settings.keyBindings[S_KEY_INVISO]])           pia1.pb |= 0x02;
+
+       if (keys[settings.keyBindings[S_KEY_AUTO_UP]])          pia2.pa |= 0x01;
+       if (keys[settings.keyBindings[S_KEY_ADVANCE]])          pia2.pa |= 0x02;
+       if (keys[settings.keyBindings[S_KEY_RIGHT_COIN]])       pia2.pa |= 0x04;
+       if (keys[settings.keyBindings[S_KEY_HS_RESET]])         pia2.pa |= 0x08;
+       if (keys[settings.keyBindings[S_KEY_LEFT_COIN]])        pia2.pa |= 0x10;
+       if (keys[settings.keyBindings[S_KEY_CENTER_COIN]])      pia2.pa |= 0x20;
+       if (keys[settings.keyBindings[S_KEY_SLAM_SWITCH]])      pia2.pa |= 0x40;
+
+       if (keys[SDL_SCANCODE_F5])                                      // Sound CPU self-test (F5)
+               soundCPU.cpuFlags |= V6808_LINE_NMI;
+       if (keys[SDL_SCANCODE_F6])                                      // Reset the 6808 (F6)
+               soundCPU.cpuFlags |= V6808_LINE_RESET;
+
+       if (paletteDirty)
+       {
+               for(uint32_t addr=0x0006; addr<0x97F7; addr++)
+               {
+                       uint16_t sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
 
-V6809 WrMem: Writing address C80E with 3F [PC=13CB, $CB00=A8]
-V6809 WrMem: Writing address C807 with 3C [PC=60B4, $CB00=90]
-V6809 WrMem: Writing address C80E with 0C [PC=014D, $CB00=80]
+                       if (sy > 5 && sy < 246)
+                       {
+                               uint32_t saddr = 8 + sx + ((sy - 6) * 320);     // Calc screen address
+                               uint8_t sb = gram[addr];
 
-V6809 WrMem: Writing address C80F with 34 [PC=014D, $CB00=80]
-V6809 WrMem: Writing address C80F with 35 [PC=014D, $CB00=80]
-V6809 WrMem: Writing address C80F with 34 [PC=0013, $CB00=A8]
-V6809 WrMem: Writing address C80F with 35 [PC=0013, $CB00=A8]
+                               scrBuffer[saddr + 0] = palette[color[sb >> 4]];
+                               scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
+                       }
+               }
 
-       C80C rom_pia_dataa
-       C80D rom_pia_ctrla
-       C80E rom_pia_datab
-             bit 0 \
-             bit 1 |
-             bit 2 |-6 bits to sound board
-             bit 3 |
-             bit 4 |
-             bit 5 /
-             bit 6 \
-             bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
-       C80F rom_pia_ctrlb
+               paletteDirty = false;
+       }
 
-CTRLA = IRQA1 (1 bit) IRQA2 (1 bit) CA2 (3 bits) DDR (1 bit) CA1 (2 bits)
+       static bool fullscreenDebounce = false;
 
+       if (keys[SDL_SCANCODE_F12])
+       {
+               if (!fullscreenDebounce)
+               {
+                       ToggleFullScreen();
+                       fullscreenDebounce = true;
+               }
+       }
+       else
+               fullscreenDebounce = false;
 
-PIA initialization:
+       RenderScreenBuffer();                                   // 1 frame = 1/60 sec ~ 16667 cycles
+       clockFrameStart = mainCPU.clock;
 
-00 -> $C80D = PIA2     -> DDR active
-00 -> $C80C = PIA2 DDR -> All input?
+       // Wait for next frame...
+       while (SDL_GetTicks() - startTicks < 16)
+               SDL_Delay(1);
 
+       startTicks = SDL_GetTicks();
+       SetCallbackTime(FrameCallback, FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC);
+}
 
+static void ScanlineCallback(void)
+{
+/*
+What we've proven so far:
 
+ - The COUNT240 IRQ *NEVER* fires on MAME driver (it can't, PACTL is never set to allow it and an IRQ never fires anywhere around scanline 240)
+ - In the demo, the IRQs fire on 0, 82 (sometimes, 87, 90-112), 128, & 192
+ - In the HS screen the IRQs fire 0, 64, 128, & 192 exactly
 */
+       double elapsedCycles = (double)(GetCurrentV6809Clock() - clockFrameStart);
+       uint32_t scanline = (uint32_t)((elapsedCycles / SCANLINE_DURATION_IN_CYCLES) + 0.5);
 
+       // N.B.: The 4 MS line toggles every 2.083 ms
+       //       Also: CA1 *never* asserts because PACTL is set to never allow IRQs
+       pia2.CB1(scanline & 0x20 ? true : false);
+       pia2.CA1(scanline >= 240 ? true : false);
+
+       SetCallbackTime(ScanlineCallback, SG2_PIA_CALLBACK_DURATION);
+}