]> Shamusworld >> Repos - thunder/commitdiff
Added V63701, YM2151 emulation, more SDL 2 fixes.
authorShamus Hammons <jlhamm@acm.org>
Thu, 17 Apr 2014 13:39:09 +0000 (08:39 -0500)
committerShamus Hammons <jlhamm@acm.org>
Thu, 17 Apr 2014 13:39:09 +0000 (08:39 -0500)
makefile
src/screen.cpp
src/thunder.cpp
src/v63701.cpp [new file with mode: 0644]
src/v63701.h [new file with mode: 0644]
src/ym2151.c [new file with mode: 0644]
src/ym2151.h [new file with mode: 0644]
test/mcu-sound.cpp [new file with mode: 0644]
test/mcu-sound.mak [new file with mode: 0644]
test/union.cpp

index 966bf3bce49a3ec8f11568915ac6a4a9c93d13e9..9004c5855cd27fd9e63924b844ed8b8c1d2df971 100644 (file)
--- a/makefile
+++ b/makefile
@@ -51,9 +51,9 @@ CPPFLAGS = -MMD -Wall -Wno-switch -Wno-non-virtual-dtor -Wno-uninitialized -Wno-
 LDFLAGS =
 
 # Ugh, let's get rid of the ref to -lcurses
-LIBS = -L/usr/local/lib `sdl2-config $(SDLLIBTYPE)` -lstdc++ -lz $(GLLIB) -lcurses
+LIBS = `sdl2-config $(SDLLIBTYPE)` -lstdc++ -lz $(GLLIB) -lcurses
 
-INCS = -I. -Isrc -I/usr/local/include
+INCS = -I. -Isrc
 
 OBJS = \
        obj/dis6808.o    \
index bc733601fa4eda7620ce1030ddcd4e65a6ee897e..f72b2bf026a57a9fb9f3a0d219dc945fb6928d60 100644 (file)
@@ -14,6 +14,7 @@
 // Who  When        What
 // ---  ----------  -----------------------------------------------------------
 // JLH  03/12/2003  Ported this steaming pile of crap from VESA to SDL
+// JLH  04/04/2014  Ported to SDL 2. Much less crappy.
 //
 
 #include "screen.h"
@@ -43,11 +44,9 @@ bool charbase;                                               // Character base pointer...
 uint8_t hScrollOffset;                         // Horizontal scroll offset
 uint8_t vScrollOffset;                         // Vertical scroll offset
 uint8_t spr_color_index;                       // Sprite color index
-uint32_t hoffsets[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };// Scroll offsets...
+//uint32_t hoffsets[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };// Scroll offsets...
 //uint32_t voffsets[8] = { 0, 320, 640, 960, 1280, 1600, 1920, 2240 };
 uint32_t voffsets[8] = { 288*0, 288*1, 288*2, 288*3, 288*4, 288*5, 288*6, 288*7 };
-//n.b.: 320 = $140
-// $000, $140, $280, $3C0, $500, $640, $780, $8C0
 
 extern bool show_text;                         // Whether or not to show text
 extern bool show_scr;                          // Whether or not to show screen
@@ -157,28 +156,14 @@ void BlitChar(SDL_Surface * scr, uint8_t * chr, uint8_t * ram)
        if (ShowGUI())
                DrawGUI();
 
-#if 0
-       if (SDL_LockSurface(scr) < 0)
-       {
-//             fprintf(stderr, "Couldn't lock the display surface: %s\n", SDL_GetError());
-//             exit(2);
-       }
-#endif
-
        // Rolling Thunder screen size is 288 x 224. Virtual is this, real may not be...
 
-       uint32_t src = 0;//(uint32_t)(hoffsets[hScrollOffset] + voffsets[vScrollOffset]);
-//     uint32_t srcAdd = 320 - VIRTUAL_SCREEN_WIDTH;
+       uint32_t src = 0;
 
        for(int i=0; i<VIRTUAL_SCREEN_HEIGHT; i++)
-       {
                for (int j=0; j<VIRTUAL_SCREEN_WIDTH; j++)
-               {
-                       scrBuffer[(i * VIRTUAL_SCREEN_WIDTH) + j] = palette[my_scr[src++]];
-               }
-
-//             src += srcAdd;
-       }
+//                     scrBuffer[(i * VIRTUAL_SCREEN_WIDTH) + j] = palette[my_scr[src++]];
+                       scrBuffer[src] = palette[my_scr[src++]];
 
        RenderScreenBuffer();
 }
@@ -282,11 +267,9 @@ void DrawSprites(uint8_t priority)
 static inline void DrawSpriteBlock(uint32_t & sprnum, uint16_t x, uint16_t y, uint16_t xStart, uint16_t xEnd, int16_t xInc)
 {
        extern uint8_t spr_rom[];
-//     uint32_t sc_addr;
 
        for(uint16_t sy=0; sy<16; sy++)
        {
-//             for(uint16_t sx=0; sx<16; sx+=2)
                for(uint16_t sx=xStart; sx<xEnd; sx+=xInc)
                {
                        uint8_t b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
@@ -310,11 +293,9 @@ static inline void DrawSpriteBlock(uint32_t & sprnum, uint16_t x, uint16_t y, ui
 static inline void DrawSpriteBlock2(uint32_t & sprnum, uint16_t x, uint16_t y, uint16_t xStart, uint16_t xEnd, int16_t xInc)
 {
        extern uint8_t spr_rom[];
-//     uint32_t sc_addr;
 
        for(uint16_t sy=0; sy<16; sy++)
        {
-//             for(uint16_t sx=0; sx<16; sx+=2)
                for(uint16_t sx=xStart; sx!=xEnd; sx+=xInc)
                {
                        uint8_t b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
@@ -341,77 +322,18 @@ static inline void DrawSpriteBlock2(uint32_t & sprnum, uint16_t x, uint16_t y, u
 void Sprite(uint32_t sprnum, uint16_t x, uint16_t y, uint8_t flip,
        uint8_t horiz_bl, uint8_t vert_bl)
 {
-       extern uint8_t spr_rom[];
-       uint32_t sc_addr;
-       sprnum <<= 7;                           // 128 bytes per sprite
+       // 128 bytes per sprite (16 x 16 chunks, 4 bits per pixel)
+       sprnum <<= 7;
 
        if (!vert_bl)
                y += 16;
 
        if (!flip)
        {
-#if 0
-               for(uint16_t sy=0; sy<16; sy++)
-               {
-                       for(uint16_t sx=0; sx<16; sx+=2)
-                       {
-                               uint8_t b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
-                               uint16_t spy = y + sy, spx = x + sx;    // Need to optimize this clipping!
-
-                               // This handles negative values, by casting as unsigned
-#if 0
-//                             if (spy > 223 || spx > 299)
-                               if (spy >= 224 || spx >= 288)
-                                       sc_addr = 0x13FFE;
-                               else
-//                                     sc_addr = spx + spy * 320;
-                                       sc_addr = spx + (spy * 288);
-#else
-                               sc_addr = ((spy >= 224) || (spx >= 288) ? 0x13FFE : spx + (spy * 288));
-#endif
-                               if (b1 != 15)
-                                       my_scr[sc_addr] = scolor[spr_color_index][b1];  // Store it
-
-                               sc_addr++;
-
-                               if (b2 != 15)
-                                       my_scr[sc_addr] = scolor[spr_color_index][b2];  // Store it
-                       }
-               }
-#else
                DrawSpriteBlock(sprnum, x, y, 0, 16, 2);
-#endif
 
                if (horiz_bl)
-               {
-#if 0
-                       for(uint16_t sy=0; sy<16; sy++)
-                       {
-                               for(uint16_t sx=16; sx<32; sx+=2)
-                               {
-                                       uint8_t b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
-                                       uint16_t spy = y + sy, spx = x + sx;
-
-//                                     if (spy > 223 || spx > 299)
-                                       if (spy > 223 || spx > 287)
-                                               sc_addr = 0x13FFE;
-                                       else
-//                                             sc_addr = spx + spy * 320;
-                                               sc_addr = spx + (spy * 288);
-
-                                       if (b1 != 15)
-                                               my_scr[sc_addr] = scolor[spr_color_index][b1];  // Store it
-
-                                       sc_addr++;
-
-                                       if (b2 != 15)
-                                               my_scr[sc_addr] = scolor[spr_color_index][b2];  // Store it
-                               }
-                       }
-#else
                        DrawSpriteBlock(sprnum, x, y, 16, 32, 2);
-#endif
-               }
                else
                        sprnum += 128;                                                          // Advance to next...
 
@@ -419,126 +341,18 @@ void Sprite(uint32_t sprnum, uint16_t x, uint16_t y, uint8_t flip,
                {
                        y += 16;                                                                        // Do next row...
 
-#if 0
-                       for(uint16_t sy=0; sy<16; sy++)
-                       {
-                               for(uint16_t sx=0; sx<16; sx+=2)
-                               {
-                                       uint8_t b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
-                                       uint16_t spy = y + sy, spx = x + sx;
-
-//                                     if (spy > 223 || spx > 299)
-                                       if (spy > 223 || spx > 287)
-                                               sc_addr = 0x13FFE;
-                                       else
-//                                             sc_addr = spx + spy * 320;
-                                               sc_addr = spx + (spy * 288);
-
-                                       if (b1 != 15)
-                                               my_scr[sc_addr] = scolor[spr_color_index][b1];  // Store it
-
-                                       sc_addr++;
-
-                                       if (b2 != 15)
-                                               my_scr[sc_addr] = scolor[spr_color_index][b2];  // Store it
-                               }
-                       }
-#else
                        DrawSpriteBlock(sprnum, x, y, 0, 16, 2);
-#endif
 
                        if (horiz_bl)
-                       {
-#if 0
-                               for(uint16_t sy=0; sy<16; sy++)
-                               {
-                                       for(uint16_t sx=16; sx<32; sx+=2)
-                                       {
-                                               uint8_t b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
-                                               uint16_t spy = y + sy, spx = x + sx;
-
-//                                             if (spy > 223 || spx > 299)
-                                               if (spy > 223 || spx > 287)
-                                                       sc_addr = 0x13FFE;
-                                               else
-//                                                     sc_addr = spx + spy * 320;
-                                                       sc_addr = spx + (spy * 288);
-
-                                               if (b1 != 15)
-                                                       my_scr[sc_addr] = scolor[spr_color_index][b1];  // Store it
-
-                                               sc_addr++;
-
-                                               if (b2 != 15)
-                                                       my_scr[sc_addr] = scolor[spr_color_index][b2];  // Store it
-                                       }
-                               }
-#else
                                DrawSpriteBlock(sprnum, x, y, 16, 32, 2);
-#endif
-                       }
                }
        }
        else    // Flip
        {
                if (horiz_bl)
-               {
-#if 0
-                       for(uint16_t sy=0; sy<16; sy++)
-                       {
-                               for(uint16_t sx=30; sx!=14; sx-=2)
-                               {
-                                       uint8_t b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
-                                       uint16_t spy = y + sy, spx = x + sx;
-
-//                                     if ((spy > 223) || (spx > 299))
-                                       if ((spy > 223) || (spx > 287))
-                                               sc_addr = 0x13FFE;
-                                       else
-//                                             sc_addr = spx + spy * 320;
-                                               sc_addr = spx + (spy * 288);
-
-                                       if (b2 != 15)
-                                               my_scr[sc_addr] = scolor[spr_color_index][b2]; // Store it
-
-                                       sc_addr++;
-
-                                       if (b1 != 15)
-                                               my_scr[sc_addr] = scolor[spr_color_index][b1]; // Store it
-                               }
-                       }
-#else
                        DrawSpriteBlock2(sprnum, x, y, 30, 14, -2);
-#endif
-               }
-
-#if 0
-               for(uint16_t sy=0; sy<16; sy++)
-               {
-                       for(uint16_t sx=14; sx!=0xFFFE; sx-=2)
-                       {
-                               uint8_t b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
-                               uint16_t spy = y + sy, spx = x + sx;
-
-//                             if ((spy > 223) || (spx > 299))
-                               if ((spy > 223) || (spx > 287))
-                                       sc_addr = 0x13FFE;
-                               else
-//                                     sc_addr = spx + spy * 320;
-                                       sc_addr = spx + (spy * 288);
 
-                               if (b2 != 15)
-                                       my_scr[sc_addr] = scolor[spr_color_index][b2]; // Store it
-
-                               sc_addr++;
-
-                               if (b1 != 15)
-                                       my_scr[sc_addr] = scolor[spr_color_index][b1]; // Store it
-                       }
-               }
-#else
                DrawSpriteBlock2(sprnum, x, y, 14, 0xFFFE, -2);
-#endif
 
                if (!horiz_bl)
                        sprnum += 128;  // If single, skip sprite...
@@ -548,63 +362,9 @@ void Sprite(uint32_t sprnum, uint16_t x, uint16_t y, uint8_t flip,
                        y += 16;      // Adjust Y coord...
 
                        if (horiz_bl)
-                       {
-#if 0
-                               for(uint16_t sy=0; sy<16; sy++)
-                               {
-                                       for(uint16_t sx=30; sx!=14; sx-=2)
-                                       {
-                                               uint8_t b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
-                                               uint16_t spy = y + sy, spx = x + sx;
-
-//                                             if ((spy > 223) || (spx > 299))
-                                               if ((spy > 223) || (spx > 287))
-                                                       sc_addr = 0x13FFE;
-                                               else
-//                                                     sc_addr = spx + spy * 320;
-                                                       sc_addr = spx + (spy * 288);
-
-                                               if (b2 != 15)
-                                                       my_scr[sc_addr] = scolor[spr_color_index][b2]; // Store it
-
-                                               sc_addr++;
-
-                                               if (b1 != 15)
-                                                       my_scr[sc_addr] = scolor[spr_color_index][b1]; // Store it
-                                       }
-                               }
-#else
                                DrawSpriteBlock2(sprnum, x, y, 30, 14, -2);
-#endif
-                       }
 
-#if 0
-                       for(uint16_t sy=0; sy<16; sy++)
-                       {
-                               for(uint16_t sx=14; sx!=0xFFFE; sx-=2)
-                               {
-                                       uint8_t b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
-                                       uint16_t spy = y + sy, spx = x + sx;
-
-//                                     if ((spy > 223) || (spx > 299))
-                                       if ((spy > 223) || (spx > 287))
-                                               sc_addr = 0x13FFE;
-                                       else
-//                                             sc_addr = spx + spy * 320;
-                                               sc_addr = spx + (spy * 288);
-
-                                       if (b2 != 15)
-                                               my_scr[sc_addr] = scolor[spr_color_index][b2]; // Store it
-
-                                       sc_addr++;
-
-                                       if (b1 != 15)
-                                               my_scr[sc_addr] = scolor[spr_color_index][b1]; // Store it
-                               }
-                       }
-#else
                        DrawSpriteBlock2(sprnum, x, y, 14, 0xFFFE, -2);
-#endif
                }
        }
 }
index 59824249291e8b3aacf52ed39f61f851a79dd39b..9439026682dab5eb03a272f204baabbe473c0a4d 100644 (file)
@@ -308,6 +308,10 @@ void WrMem(uint16_t addr, uint8_t b)
                WriteLog("\nWriteMem: CPU #1 writing $%02X to $4182!\n\n", b);
        }
 #endif
+#if 1
+if (((addr >= 0x4180) && (addr <= 0x4191)) || (addr == 0x4380))
+       printf("WriteMem: CPU #1 writing $%02X to $%04X...\n", b, addr);
+#endif
 
        if (addr == 0x6000)
                SpawnSound(GAMESOUND, gram1[0x6200], 0);                // Do voice chan 1
@@ -403,6 +407,10 @@ void WrMemB(uint16_t addr, uint8_t b)
                WriteLog("\nWriteMem: CPU #2 writing $%02X to $0182 ($4182)!\n\n", b);
        }
 #endif
+#if 1
+if (((addr >= 0x0180) && (addr <= 0x0191)) || (addr == 0x0380))
+       printf("WriteMem: CPU #2 writing $%02X to $%04X...\n", b, addr);
+#endif
 
        if (addr == 0x6000)
                SpawnSound(GAMESOUND, gram1[0x6200], 0);                // Do voice chan 1
@@ -1383,6 +1391,10 @@ WriteLog("About to enter main loop...\n");
                                        case SDL_KEYDOWN:
                                                if (event.key.keysym.sym == SDLK_ESCAPE)
                                                        running = false;
+                                               else if (event.key.keysym.sym == SDLK_F10)
+                                                       gram1[0x41A5]++;                        // Coin? (F10)
+                                               else if (event.key.keysym.sym == SDLK_c)
+                                                       gram1[0x418C]++;                        // ? (C) Start
                                        }
                                }
 
diff --git a/src/v63701.cpp b/src/v63701.cpp
new file mode 100644 (file)
index 0000000..3f01921
--- /dev/null
@@ -0,0 +1,2920 @@
+//
+// Virtual 63701 Emulator v1.0
+//
+// by James Hammons
+// (C) 2014 Underground Software
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO  WHEN        WHAT
+// ---  ----------  -----------------------------------------------------------
+// JLH  06/15/2006  Added changelog ;-)
+// JLH  06/15/2006  Scrubbed all BYTE, WORD & DWORD references from the code
+// JLH  11/13/2006  Converted core to V65C02 macro style :-)
+// JLH  11/13/2006  Converted flags to unpacked and separate flags
+// JLH  07/21/2009  Converted clock from 32-bit to 64-bit value, added possible
+//                  "don't branch" optimization
+// JLH  09/21/2009  Fixed EA_ABS macros
+// JLH  04/07/2014  Converted core to HD63701 type
+// JLH  04/16/2014  Fixed a raft of bugs, added sane timer handling
+//
+
+// This is based on V6808, since it's in the same family apparently. The
+// HD63701 has more opcodes, and apparently some built in timer facility as
+// well.
+
+// N.B.: There are some things missing from the emulation, such as the data
+//       ports, the Input Capture, RAM Control, sleep mode, processor modes,
+//       and probably some other stuff as well. What is here seems to work, and
+//       work well. It's a fair sight better than the one that's in MAME,
+//       that's for sure. :-D
+
+// Some random thoughts: Could there be a performance gain by breaking
+// out the flags in regs.cc into separate uint8_t variables (or bools)?
+// You'd have to convert on entering and exiting the emulation loop, but I
+// think the perfomance hit would be negligible compared to the gain in not
+// having to mask and shift flags all the time. Investigate after the
+// conversion to macro style opcodes is completed. :-)
+// [DONE--remain to be seen if there is any performance increase]
+
+//#define __DEBUG__
+#define TEST_DONT_BRANCH_OPTIMIZATION
+
+#include "v63701.h"
+//#include <stdio.h>           // for printf()
+
+#ifdef __DEBUG__
+#include "dis63701.h"
+//#include "log.h"
+#define WriteLog printf
+#endif
+
+// Various macros
+
+#define CLR_Z                                  (flagZ = 0)
+#define CLR_ZN                                 (flagZ = flagN = 0)
+#define CLR_ZNC                                        (flagZ = flagN = flagC = 0)
+#define CLR_NVC                                        (flagN = flagV = flagC = 0)
+#define CLR_VC                                 (flagV = flagC = 0)
+#define CLR_V                                  (flagV = 0)
+#define CLR_N                                  (flagN = 0)
+#define SET_Z(r)                               (flagZ = ((r) == 0 ? 1 : 0))
+#define SET_N(r)                               (flagN = ((r) & 0x80) >> 7)
+#define SET_V(a,b,r)                   (flagV = (((b) ^ (a) ^ (r) ^ ((r) >> 1)) & 0x80) >> 7)
+
+#define SET_C_CMP(a,b)                 (flagC = ((uint8_t)(b) < (uint8_t)(a) ? 1 : 0))
+#define SET_ZN(r)                              SET_N(r); SET_Z(r)
+#define SET_ZNC_ADD(a,b,r)             SET_N(r); SET_Z(r); SET_C_ADD(a,b)
+#define SET_ZNVC_CMP(a,b,r)            SET_N(r); SET_Z(r); SET_C_CMP(a,b); SET_V(a,b,r)
+
+#define SET_N16(r)                             (flagN = ((r) & 0x8000) >> 15)
+#define SET_V16(a,b,r)                 (flagV = (((b) ^ (a) ^ (r) ^ ((r) >> 1)) & 0x8000) >> 15)
+#define SET_C_CMP16(a,b)               (flagC = ((uint16_t)(b) < (uint16_t)(a) ? 1 : 0))
+#define SET_ZNVC_CMP16(a,b,r)  SET_N16(r); SET_Z(r); SET_C_CMP16(a,b); SET_V16(a,b,r)
+
+#define EA_IMM                         regs.pc++
+#define EA_ZP                          regs.RdMem(regs.pc++)
+#define EA_ZP_X                                (regs.RdMem(regs.pc++) + regs.x)
+#define EA_ABS                         FetchMemW(regs.pc)
+
+#define READ_IMM                       regs.RdMem(EA_IMM)
+#define READ_ZP                                regs.RdMem(EA_ZP)
+#define READ_ZP_X                      regs.RdMem(EA_ZP_X)
+#define READ_ABS                       regs.RdMem(EA_ABS)
+
+#define READ_IMM16                     FetchMemW(regs.pc);
+#define READ_ZP16                      RdMemW(EA_ZP)
+#define READ_ZP_X16                    RdMemW(EA_ZP_X)
+#define READ_ABS16                     RdMemW(EA_ABS)
+
+#define READ_IMM_WB(v)         uint16_t addr = EA_IMM;         v = regs.RdMem(addr)
+#define READ_ZP_WB(v)          uint16_t addr = EA_ZP;          v = regs.RdMem(addr)
+#define READ_ZP_X_WB(v)                uint16_t addr = EA_ZP_X;        v = regs.RdMem(addr)
+#define READ_ABS_WB(v)         uint16_t addr = EA_ABS;         v = regs.RdMem(addr)
+
+#define WRITE_BACK(d)          regs.WrMem(addr, (d))
+
+#define PULL                           regs.RdMem(regs.s++)
+#define PUSH(r)                                regs.WrMem(--regs.s, (r))
+#define PULL16                         RdMemW(regs.s);         regs.s += 2
+#define PUSH16(r)                      regs.WrMem(--regs.s, (r) & 0xFF); regs.WrMem(--regs.s, (r) >> 8)
+
+#define PACK_FLAGS                     ((regs.cc & 0xC0) | (flagH << 5) | (flagI << 4) | (flagN << 3) | (flagZ << 2) | (flagV << 1) | flagC)
+#define UNPACK_FLAGS           flagH = (regs.cc & FLAG_H) >> 5; \
+       flagI = (regs.cc & FLAG_I) >> 4; \
+       flagN = (regs.cc & FLAG_N) >> 3; \
+       flagZ = (regs.cc & FLAG_Z) >> 2; \
+       flagV = (regs.cc & FLAG_V) >> 1; \
+       flagC = (regs.cc & FLAG_C)
+
+// Private global variables
+
+static V63701REGS regs;
+static V63701REGS * regsPointer;
+static uint8_t flagH, flagI, flagN, flagZ, flagV, flagC;
+
+static uint8_t CPUCycles[256] = {
+       0,  1,  0,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
+       1,  1,  0,  0,  0,  0,  1,  1,  2,  2,  4,  1,  0,  0,  0,  0,
+       3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,
+       1,  1,  3,  3,  1,  1,  4,  4,  4,  5,  1, 10,  5,  7,  9, 12,
+       1,  0,  0,  1,  1,  0,  1,  1,  1,  1,  1,  0,  1,  1,  0,  1,
+       1,  0,  0,  1,  1,  0,  1,  1,  1,  1,  1,  0,  1,  1,  0,  1,
+       6,  7,  7,  6,  6,  7,  6,  6,  6,  6,  6,  5,  6,  4,  3,  5,
+       6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  4,  6,  4,  3,  5,
+       2,  2,  2,  3,  2,  2,  2,  0,  2,  2,  2,  2,  3,  5,  3,  0,
+       3,  3,  3,  4,  3,  3,  3,  3,  3,  3,  3,  3,  4,  5,  4,  4,
+       4,  4,  4,  5,  4,  4,  4,  4,  4,  4,  4,  4,  5,  5,  5,  5,
+       4,  4,  4,  5,  4,  4,  4,  4,  4,  4,  4,  4,  5,  6,  5,  5,
+       2,  2,  2,  3,  2,  2,  2,  0,  2,  2,  2,  2,  3,  0,  3,  0,
+       3,  3,  3,  4,  3,  3,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,
+       4,  4,  4,  5,  4,  4,  4,  4,  4,  4,  4,  4,  5,  5,  5,  5,
+       4,  4,  4,  5,  4,  4,  4,  4,  4,  4,  4,  4,  5,  5,  5,  5
+};
+
+// Private function prototypes
+
+static uint16_t RdMemW(uint16_t);
+static uint16_t FetchMemW(uint16_t);
+static inline void HandleInterrupt(uint16_t, uint16_t flag = 0);
+
+
+//
+// Read a word out of 63701 memory (little endian format)
+//
+static inline uint16_t RdMemW(uint16_t address)
+{
+       return (uint16_t)(regs.RdMem(address) << 8) | regs.RdMem(address + 1);
+}
+
+
+//
+// Fetch a word out of 63701 memory (little endian format). Increments PC
+//
+static inline uint16_t FetchMemW(uint16_t address)
+{
+       regs.pc += 2;
+       return (uint16_t)(regs.RdMem(address) << 8) | regs.RdMem(address + 1);
+}
+
+
+//
+// 63701 OPCODE IMPLEMENTATION
+//
+// NOTE: Lots of macros are used here to save a LOT of typing. Also
+//       helps speed the debugging process. :-) Because of this, combining
+//       certain lines may look like a good idea but would end in disaster.
+//       You have been warned! ;-)
+//
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Add                     |ADDA |8B 2 2|9B 3 2|AB 5 2|BB 4 3|      |A=A+M     |T TTTT|
+                        |ADDB |CB 2 2|DB 3 2|EB 5 2|FB 4 3|      |B=B+M     |T TTTT|
+Add Accumulators        |ABA  |      |      |      |      |1B 2 1|A=A+B     |T TTTT|
+*/
+
+// ADD opcodes
+
+#define OP_ADD_HANDLER(m, acc) \
+       uint16_t sum = (uint16_t)(acc) + (m); \
+       flagC = sum >> 8; \
+       flagH = (sum >> 4) & 0x01; \
+       SET_V(m, acc, sum); \
+       (acc) = sum & 0xFF; \
+       SET_ZN(acc)
+
+#define OP_ADD_HANDLER16(m, acc) \
+       uint32_t sum = (uint32_t)(acc) + (m); \
+       flagC = sum >> 16; \
+       SET_V16(m, acc, sum); \
+       (acc) = sum & 0xFFFF; \
+       SET_Z(acc); \
+       SET_N16(acc)
+
+static void Op8B(void)         // ADDA #
+{
+       uint16_t m = READ_IMM;
+       OP_ADD_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op9B(void)         // ADDA ZP
+{
+       uint16_t m = READ_ZP;
+       OP_ADD_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpAB(void)         // ADDA ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_ADD_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpBB(void)         // ADDA ABS
+{
+       uint16_t m = READ_ABS;
+       OP_ADD_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpCB(void)         // ADDB #
+{
+       uint16_t m = READ_IMM;
+       OP_ADD_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpDB(void)         // ADDB ZP
+{
+       uint16_t m = READ_ZP;
+       OP_ADD_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpEB(void)         // ADDB ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_ADD_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpFB(void)         // ADDB ABS
+{
+       uint16_t m = READ_ABS;
+       OP_ADD_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void Op1B(void)         // ABA
+{
+       OP_ADD_HANDLER(regs.d.acc.b, regs.d.acc.a);
+}
+
+
+static void Op3A(void)         // ABX
+{
+       // Seems this one does *not* affect any flags...
+       regs.x += (uint16_t)regs.d.acc.b;
+}
+
+
+static void OpC3(void)         // ADDD #
+{
+       uint16_t m = READ_IMM16;
+       OP_ADD_HANDLER16(m, regs.d.word);
+}
+
+
+static void OpD3(void)         // ADDD ZP
+{
+       uint16_t m = READ_ZP16;
+       OP_ADD_HANDLER16(m, regs.d.word);
+}
+
+
+static void OpE3(void)         // ADDD ZP, X
+{
+       uint16_t m = READ_ZP_X16;
+       OP_ADD_HANDLER16(m, regs.d.word);
+}
+
+
+static void OpF3(void)         // ADDD ABS
+{
+       uint16_t m = READ_ABS16;
+       OP_ADD_HANDLER16(m, regs.d.word);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Add with Carry          |ADCA |89 2 2|99 3 2|A9 5 2|B9 4 3|      |A=A+M+C   |T TTTT|
+                        |ADCB |C9 2 2|D9 3 2|E9 5 2|F9 4 3|      |B=B+M+C   |T TTTT|
+*/
+
+// ADC opcodes
+
+#define OP_ADC_HANDLER(m, acc) \
+       uint16_t sum = (uint16_t)acc + (m) + (uint16_t)flagC; \
+       flagC = sum >> 8; \
+       flagH = (sum >> 4) & 0x01; \
+       SET_V(m, acc, sum); \
+       acc = sum & 0xFF; \
+       SET_ZN(acc)
+
+static void Op89(void)                                                 // ADCA #
+{
+       uint16_t m = READ_IMM;
+       OP_ADC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op99(void)                                                 // ADCA ZP
+{
+       uint16_t m = READ_ZP;
+       OP_ADC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA9(void)                                                 // ADCA ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_ADC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB9(void)                                                 // ADCA ABS
+{
+       uint16_t m = READ_ABS;
+       OP_ADC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC9(void)                                                 // ADCB #
+{
+       uint16_t m = READ_IMM;
+       OP_ADC_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD9(void)                                                 // ADCB ZP
+{
+       uint16_t m = READ_ZP;
+       OP_ADC_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE9(void)                                                 // ADCB ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_ADC_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF9(void)                                                 // ADCB ABS
+{
+       uint16_t m = READ_ABS;
+       OP_ADC_HANDLER(m, regs.d.acc.b);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+And                     |ANDA |84 2 2|94 3 2|A4 5 2|B4 4 3|      |A=A+M     |  TTR |
+                        |ANDB |C4 2 2|D4 3 2|E4 5 2|F4 4 3|      |B=B+M     |  TTR |
+*/
+
+// AND opcodes
+
+#define OP_AND_HANDLER(m, acc) \
+       acc &= m; \
+       SET_ZN(acc); \
+       CLR_V
+
+static void Op84(void)         // ANDA #
+{
+       uint8_t m = READ_IMM;
+       OP_AND_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op94(void)         // ANDA ZP
+{
+       uint8_t m = READ_ZP;
+       OP_AND_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA4(void)         // ANDA ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_AND_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB4(void)         // ANDA ABS
+{
+       uint16_t m = READ_ABS;
+       OP_AND_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC4(void)         // ANDB #
+{
+       uint8_t m = READ_IMM;
+       OP_AND_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD4(void)         // ANDB ZP
+{
+       uint8_t m = READ_ZP;
+       OP_AND_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE4(void)         // ANDB ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_AND_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF4(void)         // ANDB ABS
+{
+       uint16_t m = READ_ABS;
+       OP_AND_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void Op61(void)         // AIM ZP, X (AND immediate with index)
+{
+       uint8_t m;
+       uint8_t immValue = READ_IMM;
+       READ_ZP_X_WB(m);
+       OP_AND_HANDLER(immValue, m);
+       WRITE_BACK(m);
+}
+
+
+static void Op71(void)         // AIM ZP (AND immediate with zero page)
+{
+       uint8_t m;
+       uint8_t immValue = READ_IMM;
+       READ_ZP_WB(m);
+       OP_AND_HANDLER(immValue, m);
+       WRITE_BACK(m);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Bit Test                |BITA |85 2 2|95 3 2|A5 5 2|B5 4 3|      |A+M       |  TTR |
+                        |BITB |C5 2 2|D5 3 2|E5 5 2|F5 4 3|      |B+M       |  TTR |
+*/
+
+// BIT opcodes
+
+#define OP_BIT_HANDLER(m, acc) \
+       int8_t result = acc & (m); \
+       SET_ZN(result)
+
+static void Op85(void)                                                 // BITA #
+{
+       uint8_t m = READ_IMM;
+       OP_BIT_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op95(void)                                                 // BITA ZP
+{
+       uint8_t m = READ_ZP;
+       OP_BIT_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA5(void)                                                 // BITA ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_BIT_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB5(void)                                                 // BITA ABS
+{
+       uint8_t m = READ_ABS;
+       OP_BIT_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC5(void)                                                 // BITB #
+{
+       uint8_t m = READ_IMM;
+       OP_BIT_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD5(void)                                                 // BITB ZP
+{
+       uint8_t m = READ_ZP;
+       OP_BIT_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE5(void)                                                 // BITB ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_BIT_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF5(void)                                                 // BITB ABS
+{
+       uint8_t m = READ_ABS;
+       OP_BIT_HANDLER(m, regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Clear                   |CLR  |      |      |6F 7 2|7F 6 3|      |M=00      |  RSRR|
+                        |CLRA |      |      |      |      |4F 2 1|A=00      |  RSRR|
+                        |CLRB |      |      |      |      |5F 2 1|B=00      |  RSRR|
+*/
+
+// CLR opcodes
+
+static void Op6F(void)                                                 // CLR ZP, X
+{
+       regs.WrMem(EA_ZP_X, 0);
+       CLR_NVC;
+       SET_Z(0);
+}
+
+
+static void Op7F(void)                                                 // CLR ABS
+{
+       regs.WrMem(EA_ABS, 0);
+       CLR_NVC;
+       SET_Z(0);
+}
+
+
+static void Op4F(void)                                                 // CLRA
+{
+       regs.d.acc.a = 0;
+       CLR_NVC;
+       SET_Z(0);
+}
+
+
+static void Op5F(void)                                                 // CLRB
+{
+       regs.d.acc.b = 0;
+       CLR_NVC;
+       SET_Z(0);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Compare                 |CMPA |81 2 2|91 3 2|A1 5 2|B1 4 3|      |A-M       |  TTTT|
+                        |CMPB |C1 2 2|D1 3 2|E1 5 2|F1 4 3|      |B-M       |  TTTT|
+Compare Accumulators    |CBA  |      |      |      |      |11 2 1|A-B       |  TTTT|
+*/
+
+// CMP opcodes
+
+/*
+Compare sets flags as if a subtraction had been carried out. If the value in the accumulator is equal or greater than the compared value, the Carry will be set. The equal (Z) and sign (S) flags will be set based on equality or lack thereof and the sign (i.e. A>=$80) of the accumulator.
+*/
+
+#define OP_CMP_HANDLER(m, acc) \
+       uint16_t result = acc - (m); \
+       SET_ZNVC_CMP(m, acc, result)
+
+static void Op81(void)                                                 // CMPA #
+{
+       uint8_t m = READ_IMM;
+       OP_CMP_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op91(void)                                                 // CMPA ZP
+{
+       uint8_t m = READ_ZP;
+       OP_CMP_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA1(void)                                                 // CMPA ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_CMP_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB1(void)                                                 // CMPA ABS
+{
+       uint8_t m = READ_ABS;
+       OP_CMP_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC1(void)                                                 // CMPB #
+{
+       uint8_t m = READ_IMM;
+       OP_CMP_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD1(void)                                                 // CMPB ZP
+{
+       uint8_t m = READ_ZP;
+       OP_CMP_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE1(void)                                                 // CMPB ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_CMP_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF1(void)                                                 // CMPB ABS
+{
+       uint8_t m = READ_ABS;
+       OP_CMP_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void Op11(void)                                                 // CBA
+{
+       OP_CMP_HANDLER(regs.d.acc.b, regs.d.acc.a);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Complement 1's          |COM  |      |      |63 7 2|73 6 3|      |M=-M      |  TTRS|
+                        |COMA |      |      |      |      |43 2 1|A=-A      |  TTRS|
+                        |COMB |      |      |      |      |53 2 1|B=-B      |  TTRS|
+*/
+
+// COM opcodes
+
+#define OP_COM_HANDLER(m) \
+       m = m ^ 0xFF; \
+       SET_ZN(m); \
+       CLR_V; \
+       flagC = 1
+
+static void Op63(void)                                                 // COM ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_COM_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op73(void)                                                 // COM ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_COM_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op43(void)                                                 // COMA
+{
+       OP_COM_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op53(void)                                                 // COMB
+{
+       OP_COM_HANDLER(regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Complement 2's          |NEG  |      |      |60 7 2|70 6 3|      |M=00-M    |  TT12|
+                        |NEGA |      |      |      |      |40 2 1|A=00-A    |  TT12|
+                        |NEGB |      |      |      |      |50 2 1|B=00-B    |  TT12|
+*/
+
+// NEG opcodes
+
+#define OP_NEG_HANDLER(m) \
+       m = -m; \
+       SET_ZN(m); \
+       flagV = (m == 0x80 ? 1 : 0); \
+       flagC = (m == 0x00 ? 1 : 0)
+
+static void Op60(void)                                                 // NEG ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_NEG_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op70(void)                                                 // NEG ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_NEG_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op40(void)                                                 // NEGA
+{
+       OP_NEG_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op50(void)                                                 // NEGB
+{
+       OP_NEG_HANDLER(regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Decimal Adjust          |DAA  |      |      |      |      |19 2 1|*         |  TTT3|
+*/
+
+static void Op19(void)                                                 // DAA
+{
+       uint16_t result = (uint16_t)regs.d.acc.a;
+
+       if ((regs.d.acc.a & 0x0F) > 0x09 || flagH)
+               result += 0x06;
+
+       if ((regs.d.acc.a & 0xF0) > 0x90 || flagC || ((regs.d.acc.a & 0xF0) > 0x80 && (regs.d.acc.a & 0x0F) > 0x09))
+               result += 0x60;
+
+       regs.d.acc.a = (uint8_t)result;
+       SET_ZN(result);
+       CLR_V;                                                                          // Not sure this is correct...
+       flagC |= (result & 0x100) >> 8;                         // Overwrite carry if it was 0, otherwise, ignore
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Decrement               |DEC  |      |      |6A 7 2|7A 6 3|      |M=M-1     |  TT4 |
+                        |DECA |      |      |      |      |4A 2 1|A=A-1     |  TT4 |
+                        |DECB |      |      |      |      |5A 2 1|B=B-1     |  TT4 |
+*/
+
+// DEC opcodes
+
+#define OP_DEC_HANDLER(m) \
+       m--; \
+       SET_ZN(m); \
+       flagV = (m == 0x7F ? 1 : 0)
+
+static void Op6A(void)                                                 // DEC ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_DEC_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op7A(void)                                                 // DEC ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_DEC_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op4A(void)                                                 // DECA
+{
+       OP_DEC_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op5A(void)                                                 // DECB
+{
+       OP_DEC_HANDLER(regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Exclusive OR            |EORA |88 2 2|98 3 2|A8 5 2|B8 4 3|      |A=A(+)M   |  TTR |
+                        |EORB |C8 2 2|D8 3 2|E8 5 2|F8 4 3|      |B=B(+)M   |  TTR |
+*/
+
+// EOR opcodes
+
+#define OP_EOR_HANDLER(m, acc) \
+       acc ^= m; \
+       SET_ZN(acc); \
+       CLR_V
+
+static void Op88(void)         // EORA #
+{
+       uint8_t m = READ_IMM;
+       OP_EOR_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op98(void)         // EORA ZP
+{
+       uint8_t m = READ_ZP;
+       OP_EOR_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA8(void)         // EORA ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_EOR_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB8(void)         // EORA ABS
+{
+       uint8_t m = READ_ABS;
+       OP_EOR_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC8(void)         // EORB #
+{
+       uint8_t m = READ_IMM;
+       OP_EOR_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD8(void)         // EORB ZP
+{
+       uint8_t m = READ_ZP;
+       OP_EOR_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE8(void)         // EORB ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_EOR_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF8(void)         // EORB ABS
+{
+       uint8_t m = READ_ABS;
+       OP_EOR_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void Op65(void)         // EIM ZP, X (EOR immediate with index)
+{
+       uint8_t m;
+       uint8_t immValue = READ_IMM;
+       READ_ZP_X_WB(m);
+       OP_EOR_HANDLER(immValue, m);
+       WRITE_BACK(m);
+}
+
+
+static void Op75(void)         // EIM ZP (EOR immediate with zero page)
+{
+       uint8_t m;
+       uint8_t immValue = READ_IMM;
+       READ_ZP_WB(m);
+       OP_EOR_HANDLER(immValue, m);
+       WRITE_BACK(m);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Increment               |INC  |      |      |6C 7 2|7C 6 3|      |M=M+1     |  TT5 |
+                        |INCA |      |      |      |      |4C 2 1|A=A+1     |  TT5 |
+                        |INCB |      |      |      |      |5C 2 1|B=B+1     |  TT5 |
+*/
+
+// INC opcodes
+
+#define OP_INC_HANDLER(m) \
+       m++; \
+       SET_ZN(m); \
+       flagV = (m == 0x80 ? 1 : 0)
+
+static void Op6C(void)                                                 // INC ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_INC_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op7C(void)                                                 // INC ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_INC_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op4C(void)                                                 // INCA
+{
+       OP_INC_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op5C(void)                                                 // INCB
+{
+       OP_INC_HANDLER(regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Load Accumulator        |LDAA |86 2 2|96 3 2|A6 5 2|B6 4 3|      |A=M       |  TTR |
+                        |LDAB |C6 2 2|D6 3 2|E6 5 2|F6 4 3|      |B=M       |  TTR |
+*/
+
+// LDA opcodes
+
+#define OP_LDA_HANDLER(m, acc) \
+       acc = m; \
+       SET_ZN(acc); \
+       CLR_V
+
+static void Op86(void)                                                 // LDAA #
+{
+       uint8_t m = READ_IMM;
+       OP_LDA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op96(void)                                                 // LDAA ZP
+{
+       uint8_t m = READ_ZP;
+       OP_LDA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA6(void)                                                 // LDAA ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_LDA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB6(void)                                                 // LDAA ABS
+{
+       uint8_t m = READ_ABS;
+       OP_LDA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC6(void)                                                 // LDAB #
+{
+       uint8_t m = READ_IMM;
+       OP_LDA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD6(void)                                                 // LDAB ZP
+{
+       uint8_t m = READ_ZP;
+       OP_LDA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE6(void)                                                 // LDAB ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_LDA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF6(void)                                                 // LDAB ABS
+{
+       uint8_t m = READ_ABS;
+       OP_LDA_HANDLER(m, regs.d.acc.b);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+OR, Inclusive           |ORAA |8A 2 2|9A 3 2|AA 5 2|BA 4 3|      |A=A+M     |  TTR |
+                        |ORAB |CA 2 2|DA 3 2|EA 5 2|FA 4 3|      |B=B+M     |  TTR |
+*/
+
+// ORA opcodes
+
+#define OP_ORA_HANDLER(m, acc) \
+       acc |= m; \
+       SET_ZN(acc); \
+       CLR_V
+
+static void Op8A(void)         // ORAA #
+{
+       uint8_t m = READ_IMM;
+       OP_ORA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op9A(void)         // ORAA ZP
+{
+       uint8_t m = READ_ZP;
+       OP_ORA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpAA(void)         // ORAA ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_ORA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpBA(void)         // ORAA ABS
+{
+       uint8_t m = READ_ABS;
+       OP_ORA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpCA(void)         // ORAB #
+{
+       uint8_t m = READ_IMM;
+       OP_ORA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpDA(void)         // ORAB ZP
+{
+       uint8_t m = READ_ZP;
+       OP_ORA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpEA(void)         // ORAB ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_ORA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpFA(void)         // ORAB ABS
+{
+       uint8_t m = READ_ABS;
+       OP_ORA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void Op62(void)         // OIM ZP, X (ORA immediate with index)
+{
+       uint8_t m;
+       uint8_t immValue = READ_IMM;
+       READ_ZP_X_WB(m);
+       OP_ORA_HANDLER(immValue, m);
+       WRITE_BACK(m);
+}
+
+
+static void Op72(void)         // OIM ZP (ORA immediate with zero page)
+{
+       uint8_t m;
+       uint8_t immValue = READ_IMM;
+       READ_ZP_WB(m);
+       OP_ORA_HANDLER(immValue, m);
+       WRITE_BACK(m);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Push Data               |PSHA |      |      |      |      |36 4 1|Msp=A, *- |      |
+                        |PSHB |      |      |      |      |37 4 1|Msp=B, *- |      |
+Pull Data               |PULA |      |      |      |      |32 4 1|A=Msp, *+ |      |
+                        |PULB |      |      |      |      |33 4 1|B=Msp, *+ |      |
+*/
+
+static void Op36(void)                                                 // PSHA
+{
+       PUSH(regs.d.acc.a);
+}
+
+
+static void Op37(void)                                                 // PSHB
+{
+       PUSH(regs.d.acc.b);
+}
+
+
+static void Op32(void)                                                 // PULA
+{
+       regs.d.acc.a = PULL;
+}
+
+
+static void Op33(void)                                                 // PULB
+{
+       regs.d.acc.b = PULL;
+}
+
+
+static void Op38(void)                                                 // PULX
+{
+       regs.x = PULL16;
+}
+
+
+static void Op3C(void)                                                 // PSHX
+{
+       PUSH16(regs.x);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Rotate Left             |ROL  |      |      |69 7 2|79 6 3|      |Memory  *1|  TT6T|
+                        |ROLA |      |      |      |      |49 2 1|Accum A *1|  TT6T|
+                        |ROLB |      |      |      |      |59 2 1|Accum B *1|  TT6T|
+*/
+
+// ROL opcodes
+
+#define OP_ROL_HANDLER(m) \
+       uint8_t newCarry = (m & 0x80) >> 7; \
+       m = (m << 1) | flagC; \
+       SET_ZN(m); \
+       flagC = newCarry; \
+       flagV = flagN ^ flagC
+
+static void Op69(void)                                                 // ROL ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_ROL_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op79(void)                                                 // ROL ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_ROL_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op49(void)                                                 // ROLA
+{
+       OP_ROL_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op59(void)                                                 // ROLB
+{
+       OP_ROL_HANDLER(regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Rotate Right            |ROR  |      |      |66 7 2|76 6 3|      |Memory  *2|  TT6T|
+                        |RORA |      |      |      |      |46 2 1|Accum A *2|  TT6T|
+                        |RORB |      |      |      |      |56 2 1|Accum B *2|  TT6T|
+*/
+
+// ROR opcodes
+
+#define OP_ROR_HANDLER(m) \
+       uint8_t newCarry = m & 0x01; \
+       m = (m >> 1) | (flagC << 7); \
+       SET_ZN(m); \
+       flagC = newCarry; \
+       flagV = flagN ^ flagC
+
+static void Op66(void)                                                 // ROR ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_ROR_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op76(void)                                                 // ROR ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_ROR_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op46(void)                                                 // RORA
+{
+       OP_ROR_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op56(void)                                                 // RORB
+{
+       OP_ROR_HANDLER(regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Arithmetic Shift Left   |ASL  |      |      |68 7 2|78 6 3|      |Memory  *3|  TT6T|
+                        |ASLA |      |      |      |      |48 2 1|Accum A *3|  TT6T|
+                        |ASLB |      |      |      |      |58 2 1|Accum B *3|  TT6T|
+*/
+
+// ASL opcodes
+
+#define OP_ASL_HANDLER(m) \
+       uint8_t newCarry = (m & 0x80) >> 7; \
+       m <<= 1; \
+       SET_ZN(m); \
+       flagC = newCarry; \
+       flagV = flagN ^ flagC
+
+static void Op68(void)         // ASL ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_ASL_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op78(void)         // ASL ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_ASL_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op48(void)         // ASLA
+{
+       OP_ASL_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op58(void)         // ASLB
+{
+       OP_ASL_HANDLER(regs.d.acc.b);
+}
+
+
+static void Op05(void)         // ASLD
+{
+       uint8_t newCarry = (regs.d.word & 0x8000) >> 15;
+       regs.d.word <<= 1;
+       SET_Z(regs.d.word);
+       SET_N16(regs.d.word);
+       flagC = newCarry;
+       flagV = flagN ^ flagC;
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Arithmetic Shift Right  |ASR  |      |      |67 7 2|77 6 3|      |Memory  *4|  TT6T|
+                        |ASRA |      |      |      |      |47 2 1|Accum A *4|  TT6T|
+                        |ASRB |      |      |      |      |57 2 1|Accum B *4|  TT6T|
+*/
+
+// ASR opcodes
+
+#define OP_ASR_HANDLER(m) \
+       uint8_t newCarry = m & 0x01; \
+       m = (m >> 1) | (m & 0x80); \
+       SET_ZN(m); \
+       flagC = newCarry; \
+       flagV = flagN ^ flagC
+
+static void Op67(void)         // ASR ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_ASR_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op77(void)         // ASR ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_ASR_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op47(void)         // ASRA
+{
+       OP_ASR_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op57(void)         // ASRB
+{
+       OP_ASR_HANDLER(regs.d.acc.b);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Logic Shift Right       |LSR  |      |      |64 7 2|74 6 3|      |Memory  *5|  TT6T|
+                        |LSRA |      |      |      |      |44 2 1|Accum A *5|  TT6T|
+                        |LSRB |      |      |      |      |54 2 1|Accum B *5|  TT6T|
+*/
+
+// LSR opcodes
+
+#define OP_LSR_HANDLER(m) \
+       uint8_t newCarry = m & 0x01; \
+       m >>= 1; \
+       SET_ZN(m); \
+       flagC = newCarry; \
+       flagV = flagN ^ flagC
+
+static void Op64(void)         // LSR ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_LSR_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op74(void)         // LSR ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_LSR_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op44(void)         // LSRA
+{
+       OP_LSR_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op54(void)         // LSRB
+{
+       OP_LSR_HANDLER(regs.d.acc.b);
+}
+
+
+static void Op04(void)         // LSRD
+{
+       uint8_t newCarry = regs.d.word & 0x01;
+       regs.d.word >>= 1;
+       SET_Z(regs.d.word);
+       flagN = 0;
+       flagC = newCarry;
+       flagV = flagN ^ flagC;
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Store Accumulator       |STAA |      |97 4 2|A7 6 2|B7 5 3|      |M=A       |  TTR |
+                        |STAB |      |D7 4 2|E7 6 2|F7 5 3|      |M=B       |  TTR |
+*/
+
+static void Op97(void)         // STAA ZP
+{
+       regs.WrMem(EA_ZP, regs.d.acc.a);
+}
+
+
+static void OpA7(void)         // STAA ZP, X
+{
+       regs.WrMem(EA_ZP_X, regs.d.acc.a);
+}
+
+
+static void OpB7(void)         // STAA ABS
+{
+       regs.WrMem(EA_ABS, regs.d.acc.a);
+}
+
+
+static void OpD7(void)         // STAB ZP
+{
+       regs.WrMem(EA_ZP, regs.d.acc.b);
+}
+
+
+static void OpE7(void)         // STAB ZP, X
+{
+       regs.WrMem(EA_ZP_X, regs.d.acc.b);
+}
+
+
+static void OpF7(void)         // STAB ABS
+{
+       regs.WrMem(EA_ABS, regs.d.acc.b);
+}
+
+
+// These are illegal instructions!
+#if 0
+static void Op87(void)         // STA #
+{
+       // What does this even mean? Seems the M.A.M.E. guys think it's something
+       // like so:
+       uint16_t effectiveAddress = regs.pc;
+       regs.pc++;
+       regs.WrMem(effectiveAddress, regs.d.acc.a);
+       SET_ZN(regs.d.acc.a);
+       flagV = 0;
+       // So, basically, what this does is change the immediate value in the
+       // STA #. Seems like a completely useless thing to me.
+}
+
+
+static void OpC7(void)         // STB #
+{
+       uint16_t effectiveAddress = regs.pc;
+       regs.pc++;
+       regs.WrMem(effectiveAddress, regs.d.acc.b);
+       SET_ZN(regs.d.acc.b);
+       flagV = 0;
+}
+#endif
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Subtract                |SUBA |80 2 2|90 3 2|A0 5 2|B0 4 3|      |A=A-M     |  TTTT|
+                        |SUBB |C0 2 2|D0 3 2|E0 5 2|F0 4 3|      |B=B-M     |  TTTT|
+Subtract Accumulators   |SBA  |      |      |      |      |10 2 1|A=A-B     |  TTTT|
+*/
+
+// SUB opcodes
+
+#define OP_SUB_HANDLER(m, acc) \
+       uint16_t sum = (uint16_t)acc - (m); \
+       flagC = sum >> 15; \
+       SET_V(m, acc, sum); \
+       acc = (uint8_t)sum; \
+       SET_ZN(acc)
+
+#define OP_SUB_HANDLER16(m, acc) \
+       uint32_t sum = (uint32_t)acc - (m); \
+       flagC = sum >> 31; \
+       SET_V16(m, acc, sum); \
+       acc = (uint16_t)sum; \
+       SET_Z(acc); \
+       SET_N16(acc)
+
+static void Op80(void)         // SUBA #
+{
+       uint16_t m = READ_IMM;
+       OP_SUB_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op90(void)         // SUBA ZP
+{
+       uint16_t m = READ_ZP;
+       OP_SUB_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA0(void)         // SUBA ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_SUB_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB0(void)         // SUBA ABS
+{
+       uint16_t m = READ_ABS;
+       OP_SUB_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC0(void)         // SUBB #
+{
+       uint16_t m = READ_IMM;
+       OP_SUB_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD0(void)         // SUBB ZP
+{
+       uint16_t m = READ_ZP;
+       OP_SUB_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE0(void)         // SUBB ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_SUB_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF0(void)         // SUBB ABS
+{
+       uint16_t m = READ_ABS;
+       OP_SUB_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void Op10(void)         // SBA
+{
+       OP_SUB_HANDLER(regs.d.acc.b, regs.d.acc.a);
+}
+
+
+static void Op83(void)         // SUBD #
+{
+       uint16_t m = READ_IMM16;
+       OP_SUB_HANDLER16(m, regs.d.word);
+}
+
+
+static void Op93(void)         // SUBD ZP
+{
+       uint16_t m = READ_ZP16;
+       OP_SUB_HANDLER16(m, regs.d.word);
+}
+
+
+static void OpA3(void)         // SUBD ZP, X
+{
+       uint16_t m = READ_ZP_X16;
+       OP_SUB_HANDLER16(m, regs.d.word);
+}
+
+
+static void OpB3(void)         // SUBD ABS
+{
+       uint16_t m = READ_ABS16;
+       OP_SUB_HANDLER16(m, regs.d.word);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Subtract with Carry     |SBCA |82 2 2|92 3 2|A2 5 2|B2 4 3|      |A=A-M-C   |  TTTT|
+                        |SBCB |C2 2 2|D2 3 2|E2 5 2|F2 4 3|      |B=B-M-C   |  TTTT|
+*/
+
+// SBC opcodes
+
+#define OP_SBC_HANDLER(m, acc) \
+       uint16_t sum = (uint16_t)acc - (m) - (uint16_t)flagC; \
+       flagC = sum >> 15; \
+       SET_V(m, acc, sum); \
+       acc = (uint8_t)sum; \
+       SET_ZN(acc)
+
+static void Op82(void)                                                 // SBCA #
+{
+       uint16_t m = READ_IMM;
+       OP_SBC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op92(void)                                                 // SBCA ZP
+{
+       uint16_t m = READ_ZP;
+       OP_SBC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA2(void)                                                 // SBCA ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_SBC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB2(void)                                                 // SBCA ABS
+{
+       uint16_t m = READ_ABS;
+       OP_SBC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC2(void)                                                 // SBCB #
+{
+       uint16_t m = READ_IMM;
+       OP_SBC_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD2(void)                                                 // SBCB ZP
+{
+       uint16_t m = READ_ZP;
+       OP_SBC_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE2(void)                                                 // SBCB ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_SBC_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF2(void)                                                 // SBCB ABS
+{
+       uint16_t m = READ_ABS;
+       OP_SBC_HANDLER(m, regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Transfer Accumulators   |TAB  |      |      |      |      |16 2 1|B=A       |  TTR |
+                        |TBA  |      |      |      |      |17 2 1|A=B       |  TTR |
+*/
+
+static void Op16(void)                                                 // TAB
+{
+       regs.d.acc.b = regs.d.acc.a;
+       SET_ZN(regs.d.acc.b);
+       CLR_V;
+}
+
+
+static void Op17(void)                                                 // TBA
+{
+       regs.d.acc.a = regs.d.acc.b;
+       SET_ZN(regs.d.acc.a);
+       CLR_V;
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Test, Zero/Minus        |TST  |      |      |6D 7 2|7D 6 3|      |M-00      |  TTRR|
+                        |TSTA |      |      |      |      |4D 2 1|A-00      |  TTRR|
+                        |TSTB |      |      |      |      |5D 2 1|B-00      |  TTRR|
+*/
+
+// TST opcodes
+
+#define OP_TST_HANDLER(m) \
+       uint8_t result = m; \
+       SET_ZN(result); \
+       CLR_VC
+
+#define OP_TST_IMM_HANDLER(m, acc) \
+       uint8_t result = m & acc; \
+       SET_ZN(result); \
+       CLR_VC
+
+static void Op6D(void)                                                 // TST ZP, X
+{
+#if 0
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_TST_HANDLER(m);
+       WRITE_BACK(m);
+#else
+       OP_TST_HANDLER(READ_ZP_X);
+#endif
+}
+
+
+static void Op7D(void)                                                 // TST ABS
+{
+#if 0
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_TST_HANDLER(m);
+       WRITE_BACK(m);
+#else
+       OP_TST_HANDLER(READ_ABS);
+#endif
+}
+
+
+static void Op4D(void)                                                 // TSTA
+{
+       OP_TST_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op5D(void)                                                 // TSTB
+{
+       OP_TST_HANDLER(regs.d.acc.b);
+}
+
+
+static void Op6B(void)         // TIM ZP, X (TST immediate with index)
+{
+//     uint8_t m;
+       uint8_t immValue = READ_IMM;
+//     READ_ZP_X_WB(m);
+       OP_TST_IMM_HANDLER(immValue, READ_ZP_X);
+//     WRITE_BACK(m);
+}
+
+
+static void Op7B(void)         // TIM ZP (TST immediate with zero page)
+{
+//     uint8_t m;
+       uint8_t immValue = READ_IMM;
+//     READ_ZP_WB(m);
+       OP_TST_IMM_HANDLER(immValue, READ_ZP);
+//     WRITE_BACK(m);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Compare Index Register  |CPX  |8C 3 3|9C 4 2|AC 6 2|BC 5 3|      |Formula 1 |  7T8 |
+*/
+
+// CPX opcodes
+
+/*
+Compare sets flags as if a subtraction had been carried out. If the value in the X register is equal or greater than the compared value, the Carry will be set. The equal (Z) and sign (S) flags will be set based on equality or lack thereof and the sign (i.e. X>=$8000) of the X register.
+*/
+
+#define OP_CPX_HANDLER(m) \
+       uint32_t result = regs.x - (m); \
+       SET_ZNVC_CMP16(m, regs.x, result)
+
+static void Op8C(void)                                                 // CPX #
+{
+       uint16_t m = READ_IMM16;
+       OP_CPX_HANDLER(m);
+}
+
+
+static void Op9C(void)                                                 // CPX ZP
+{
+       uint16_t m = READ_ZP16;
+       OP_CPX_HANDLER(m);
+}
+
+
+static void OpAC(void)                                                 // CPX ZP, X
+{
+       uint16_t m = READ_ZP_X16;
+       OP_CPX_HANDLER(m);
+}
+
+
+static void OpBC(void)                                                 // CPX ABS
+{
+       uint16_t m = READ_ABS16;
+       OP_CPX_HANDLER(m);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Decrement Index Register|DEX  |      |      |      |      |09 4 1|X=X-1     |   T  |
+Dec Stack Pointer       |DES  |      |      |      |      |34 4 1|SP=SP-1   |      |
+Inc Index Regster       |INX  |      |      |      |      |08 4 1|X=X+1     |   T  |
+Inc Stack Pointer       |INS  |      |      |      |      |31 4 1|SP=SP+1   |      |
+*/
+
+static void Op09(void)                                                 // DEX
+{
+       regs.x--;
+       SET_Z(regs.x);
+}
+
+
+static void Op34(void)                                                 // DES
+{
+       regs.s--;
+}
+
+
+static void Op08(void)                                                 // INX
+{
+       regs.x++;
+       SET_Z(regs.x);
+}
+
+
+static void Op31(void)                                                 // INS
+{
+       regs.s++;
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Load Index Register     |LDX  |CE 3 3|DE 4 2|EE 6 2|FE 5 3|      |Formula 2 |  9TR |
+Load Stack Pointer      |LDS  |8E 3 3|9E 4 2|AE 6 2|BE 5 3|      |Formula 3 |  9TR |
+*/
+
+// LD* opcode handler
+
+#define OP_LD_HANDLER(acc) \
+       SET_N16(acc); \
+       SET_Z(acc); \
+       CLR_V
+
+static void OpCE(void)         // LDX #
+{
+       regs.x = READ_IMM16;
+       OP_LD_HANDLER(regs.x);
+}
+
+
+static void OpDE(void)         // LDX ZP
+{
+       regs.x = READ_ZP16;
+       OP_LD_HANDLER(regs.x);
+}
+
+
+static void OpEE(void)         // LDX ZP, X
+{
+       regs.x = READ_ZP_X16;
+       OP_LD_HANDLER(regs.x);
+}
+
+
+static void OpFE(void)         // LDX ABS
+{
+       regs.x = READ_ABS16;
+       OP_LD_HANDLER(regs.x);
+}
+
+
+static void Op8E(void)         // LDS #
+{
+       regs.s = READ_IMM16;
+       OP_LD_HANDLER(regs.s);
+}
+
+
+static void Op9E(void)         // LDS ZP
+{
+       regs.s = READ_ZP16;
+       OP_LD_HANDLER(regs.s);
+}
+
+
+static void OpAE(void)         // LDS ZP, X
+{
+       regs.s = READ_ZP_X16;
+       OP_LD_HANDLER(regs.s);
+}
+
+
+static void OpBE(void)         // LDS ABS
+{
+       regs.s = READ_ABS16;
+       OP_LD_HANDLER(regs.s);
+}
+
+
+static void OpCC(void)         // LDD #
+{
+       regs.d.word = READ_IMM16;
+       OP_LD_HANDLER(regs.d.word);
+}
+
+
+static void OpDC(void)         // LDD ZP
+{
+       regs.d.word = READ_ZP16;
+       OP_LD_HANDLER(regs.d.word);
+}
+
+
+static void OpEC(void)         // LDD ZP, X
+{
+       regs.d.word = READ_ZP_X16;
+       OP_LD_HANDLER(regs.d.word);
+}
+
+
+static void OpFC(void)         // LDD ABS
+{
+       regs.d.word = READ_ABS16;
+       OP_LD_HANDLER(regs.d.word);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Store Index Register    |STX  |      |DF 5 2|EF 7 2|FF 6 3|      |Formula 4 |  9TR |
+Store Stack Pointer     |STS  |      |9F 5 2|AF 7 2|BF 6 3|      |Formula 5 |  9TR |
+*/
+
+// ST* opcode handler
+
+#define OP_ST_HANDLER(m, acc) \
+       regs.WrMem(m + 0, acc >> 8); \
+       regs.WrMem(m + 1, acc & 0xFF); \
+       SET_N16(acc); \
+       SET_Z(acc); \
+       CLR_V
+
+static void OpDF(void)         // STX ZP
+{
+       uint16_t m = EA_ZP;
+       OP_ST_HANDLER(m, regs.x);
+}
+
+
+static void OpEF(void)         // STX ZP, X
+{
+       uint16_t m = EA_ZP_X;
+       OP_ST_HANDLER(m, regs.x);
+}
+
+
+static void OpFF(void)         // STX ABS
+{
+       uint16_t m = EA_ABS;
+       OP_ST_HANDLER(m, regs.x);
+}
+
+
+static void Op9F(void)         // STS ZP
+{
+       uint16_t m = EA_ZP;
+       OP_ST_HANDLER(m, regs.s);
+}
+
+
+static void OpAF(void)         // STS ZP, X
+{
+       uint16_t m = EA_ZP_X;
+       OP_ST_HANDLER(m, regs.s);
+}
+
+
+static void OpBF(void)         // STS ABS
+{
+       uint16_t m = EA_ABS;
+       OP_ST_HANDLER(m, regs.s);
+}
+
+
+// These are illegal instructions!
+#if 0
+// Store immediate--nonsensical opcodes :-P
+static void Op8F(void)         // STS #
+{
+       uint16_t effectiveAddress = regs.pc;
+       regs.pc += 2;
+       OP_ST_HANDLER(effectiveAddress, regs.s);
+}
+
+
+static void OpCF(void)         // STX #
+{
+       uint16_t effectiveAddress = regs.pc;
+       regs.pc += 2;
+       OP_ST_HANDLER(effectiveAddress, regs.x);
+}
+
+
+static void OpCD(void)         // STD #
+{
+       uint16_t effectiveAddress = regs.pc;
+       regs.pc += 2;
+       OP_ST_HANDLER(effectiveAddress, regs.d.word);
+}
+#endif
+
+
+static void OpDD(void)         // STD ZP
+{
+       uint16_t m = EA_ZP;
+       OP_ST_HANDLER(m, regs.d.word);
+}
+
+
+static void OpED(void)         // STD ZP, X
+{
+       uint16_t m = EA_ZP_X;
+       OP_ST_HANDLER(m, regs.d.word);
+}
+
+
+static void OpFD(void)         // STD ABS
+{
+       uint16_t m = EA_ABS;
+       OP_ST_HANDLER(m, regs.d.word);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Index Reg > Stack Pnter |TXS  |      |      |      |      |35 4 1|SP=X-1    |      |
+Stack Ptr > Index Regtr |TSX  |      |      |      |      |30 4 1|X=SP+1    |      |
+*/
+
+static void Op35(void)                                                 // TXS
+{
+       regs.s = regs.x - 1;
+}
+
+
+static void Op30(void)                                                 // TSX
+{
+       regs.x = regs.s + 1;
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Always                  |BRA  |      |20 4 2|      |      |      |none      |      |
+Carry is Clear          |BCC  |      |24 4 2|      |      |      |C=0       |      |
+Carry is Set            |BCS  |      |25 4 2|      |      |      |C=1       |      |
+Equals Zero             |BEQ  |      |27 4 2|      |      |      |Z=1       |      |
+Greater or Equal to Zero|BGE  |      |2C 4 2|      |      |      |N(+)V=0   |      |
+Greater than Zero       |BGT  |      |2E 4 2|      |      |      |Z+N(+)V=0 |      |
+Higher                  |BHI  |      |22 4 2|      |      |      |C+Z=0     |      |
+Less or Equal than Zero |BLE  |      |2F 4 2|      |      |      |Z+N(+)V=1 |      |
+Lower or Same           |BLS  |      |23 4 2|      |      |      |C+Z=1     |      |
+Less Than Zero          |BLT  |      |2D 4 2|      |      |      |N(+)V=1   |      |
+Minus                   |BMI  |      |2B 4 2|      |      |      |N=1       |      |
+Not Zero                |BNE  |      |26 4 2|      |      |      |Z=0       |      |
+Overflow Clear          |BVC  |      |28 4 2|      |      |      |V=0       |      |
+Overflow Set            |BVS  |      |29 4 2|      |      |      |V=1       |      |
+Plus                    |BPL  |      |2A 4 2|      |      |      |N=0       |      |
+*/
+
+static void Op20(void)         // BRA
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+       regs.pc += m;
+}
+
+
+static void Op21(void)         // BRN
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+//     regs.pc += m;
+}
+
+
+static void Op24(void)         // BCC
+{
+// NOTE: We can optimize this by following the maxim: "Don't branch!" by
+//       converting the boolean result into a multiplication. The only way to
+//       know if this is a win is to do some profiling both with and without
+//       the optimization.
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+//Note sure if the ! operator will do what we want, so we use ^ 1
+       regs.pc += m * (flagC ^ 0x01);
+#else
+       if (!flagC)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op25(void)         // BCS
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagC);
+#else
+       if (flagC)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op27(void)         // BEQ
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagZ);
+#else
+       if (flagZ)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op2C(void)         // BGE
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * ((flagN ^ flagV) ^ 0x01);
+#else
+       if (!(flagN ^ flagV))
+               regs.pc += m;
+#endif
+}
+
+
+static void Op2E(void)         // BGT
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * ((flagZ | (flagN ^ flagV)) ^ 0x01);
+#else
+       if (!(flagZ | (flagN ^ flagV)))
+               regs.pc += m;
+#endif
+}
+
+
+static void Op22(void)         // BHI
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * ((flagZ | flagC) ^ 0x01);
+#else
+       if (!(flagZ | flagC))
+               regs.pc += m;
+#endif
+}
+
+
+static void Op2F(void)         // BLE
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagZ | (flagN ^ flagV));
+#else
+       if (flagZ | (flagN ^ flagV))
+               regs.pc += m;
+#endif
+}
+
+
+static void Op23(void)         // BLS
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagZ | flagC);
+#else
+       if (flagZ | flagC)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op2D(void)         // BLT
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagN ^ flagV);
+#else
+       if (flagN ^ flagV)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op2B(void)         // BMI
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagN);
+#else
+       if (flagN)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op26(void)         // BNE
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagZ ^ 0x01);
+#else
+       if (!flagZ)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op28(void)         // BVC
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagV ^ 0x01);
+#else
+       if (!flagV)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op29(void)         // BVS
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagV);
+#else
+       if (flagV)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op2A(void)         // BPL
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagN ^ 0x01);
+#else
+       if (!flagN)
+               regs.pc += m;
+#endif
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Branch to Subroutine    |BSR  |      |8D 8 2|      |      |      |          |      |
+Jump                    |JMP  |      |      |6E 4 2|7E 3 3|      |          |      |
+Jump to Subroutine      |JSR  |      |      |AD 8 2|BD 9 3|      |          |      |
+*/
+
+static void Op8D(void)         // BSR
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+       PUSH16(regs.pc);
+       regs.pc += m;
+}
+
+
+static void Op6E(void)         // JMP ZP, X
+{
+       regs.pc = EA_ZP_X;
+}
+
+
+static void Op7E(void)         // JMP ABS
+{
+       regs.pc = EA_ABS;
+}
+
+
+static void Op9D(void)         // JSR ZP
+{
+       uint16_t m = (uint16_t)EA_ZP;
+       PUSH16(regs.pc);
+       regs.pc = m;
+}
+
+
+static void OpAD(void)         // JSR ZP, X
+{
+       uint16_t m = EA_ZP_X;
+       PUSH16(regs.pc);
+       regs.pc = m;
+}
+
+
+static void OpBD(void)         // JSR ABS
+{
+       uint16_t m = EA_ABS;
+       PUSH16(regs.pc);
+       regs.pc = m;
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+No Operation            |NOP  |      |      |      |      |01 2 1|          |      |
+Return from Interrupt   |RTI  |      |      |      |      |3B A 1|          |AAAAAA|
+Return from Subroutine  |RTS  |      |      |      |      |39 5 1|          |      |
+Software Interrupt      |SWI  |      |      |      |      |3F C 1|          | S    |
+Wait For Interrupt      |WAI  |      |      |      |      |3E 9 1|          | B    |
+*/
+
+static void Op01(void)         // NOP
+{
+}
+
+
+static void Op3B(void)         // RTI
+{
+       regs.cc = PULL;
+       regs.d.acc.b  = PULL;
+       regs.d.acc.a  = PULL;
+       regs.x  = PULL16;
+       regs.pc = PULL16;
+       UNPACK_FLAGS;
+}
+
+
+static void Op39(void)         // RTS
+{
+       regs.pc = PULL16;
+}
+
+
+static void Op3F(void)         // SWI
+{
+       // It seems that the SWI is non-maskable, unlike the IRQ...
+#if 0
+       regs.cc = PACK_FLAGS;                   // Mash flags back into the CC register
+       PUSH16(regs.pc);                                // Save all regs...
+       PUSH16(regs.x);
+       PUSH(regs.d.acc.b);
+       PUSH(regs.d.acc.a);
+       PUSH(regs.cc);
+       regs.pc = RdMemW(0xFFFA);               // And do it!
+       flagI = 1;                                              // Also, set IRQ inhibit
+#endif
+       HandleInterrupt(0xFFFA);
+}
+
+
+static void Op3E(void)         // WAI
+{
+#ifdef __DEBUG__
+WriteLog("*** WAI STATE ASSERTED ***\n");
+#endif
+       regs.cc = PACK_FLAGS;                   // Mash flags back into the CC register
+       PUSH16(regs.pc);                                // Save all regs...
+       PUSH16(regs.x);
+       PUSH(regs.d.acc.b);
+       PUSH(regs.d.acc.a);
+       PUSH(regs.cc);
+       regs.cpuFlags |= V63701_STATE_WAI;      // And signal that we're in WAI mode
+}
+
+
+// Multiply opcode
+
+static void Op3D(void)         // MUL
+{
+       regs.d.word = (uint16_t)regs.d.acc.a * (uint16_t)regs.d.acc.b;
+       flagC = regs.d.acc.b >> 7;      // bug?  No, this is how it really does it
+}
+
+
+// Exchange X and D opcode
+
+static void Op18(void)         // XGDX
+{
+       // This doesn't affect flags apparently
+       uint16_t temp = regs.x;
+       regs.x = regs.d.word;
+       regs.d.word = temp;
+}
+
+
+// Sleep opcode (similar to WAI)
+
+static void Op1A(void)         // SLP
+{
+       // Apparently doesn't stack the state, so no need for RTI
+       // It would seem that this opcode is intended for use in interrupt
+       // routines, as it prevents stacking the state when one happens.
+       // (Actually, this is used to put the MCU into a low power "sleep" mode,
+       // where it stays until an interrupt or RESET occurs.)
+       regs.cpuFlags |= V63701_STATE_WAI;
+}
+
+
+// Undocumented opcode ($12 & $13)
+
+static void OpUN(void)         // Undocumented
+{
+       regs.x += regs.RdMem(regs.s + 1);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Clear Carry             |CLC  |      |      |      |      |0C 2 1|C=0       |     R|
+Clear Interrupt         |CLI  |      |      |      |      |0E 2 1|I=0       | R    |
+Clear Overflow          |CLV  |      |      |      |      |0A 2 1|V=0       |    R |
+Set Carry               |SEC  |      |      |      |      |0D 2 1|C=1       |     S|
+Set Interrupt           |SEI  |      |      |      |      |0F 2 1|I=1       | S    |
+Set Overflow            |SEV  |      |      |      |      |0B 2 1|V=1       |    S |
+CCR=Accumulator A       |TAP  |      |      |      |      |06 2 1|CCR=A     |CCCCCC|
+Accumlator A=CCR        |TPA  |      |      |      |      |07 2 1|A=CCR     |      |
+*/
+
+static void Op0C(void)                                                 // CLC
+{
+       flagC = 0;
+}
+
+
+static void Op0E(void)                                                 // CLI
+{
+       flagI = 0;
+}
+
+
+static void Op0A(void)                                                 // CLV
+{
+       flagV = 0;
+}
+
+
+static void Op0D(void)                                                 // SEC
+{
+       flagC = 1;
+}
+
+
+static void Op0F(void)                                                 // SEI
+{
+       flagI = 1;
+}
+
+
+static void Op0B(void)                                                 // SEV
+{
+       flagV = 1;
+}
+
+
+static void Op06(void)                                                 // TAP
+{
+       regs.cc = regs.d.acc.a;
+       UNPACK_FLAGS;
+}
+
+
+static void Op07(void)                                                 // TPA
+{
+       regs.d.acc.a = PACK_FLAGS;
+}
+
+
+/*
+  OP  Operation Code, in Hexadecimal
+  ~   Number of MPU cycles required
+  #   Number of program bytes required
+  +   Arithmetic Plus
+  -   Arithmetic Minus
+  +   Boolean AND
+  Msp Contents of Memory pointed to be Stack Pointer
+  +   Boolean Inclusive OR
+  (+) Boolean Exclusive OR (XOR)
+  *   Converts Binary Addition of BCD Characters into BCD Format
+  *-  SP=SP-1
+  *+  SP=SP+1
+
+  Condition Code Register Legend
+     Not Affected
+   R Reset (0, Low)
+   S Set   (1, High)
+   T Tests and sets if True, cleared otherise
+   1 Test: Result=10000000?
+   2 Test: Result=00000000?
+   3 Test: Decimal value of most significant BCD character greater than nine?
+           (Not cleared if previously set)
+   4 Test: Operand=10000000 prior to execution?
+   5 Test: Operand=01111111 prior to execution?
+   6 Test: Set equal to result or N(+)C after shift has occurred.
+   7 Test: Sign bit of most significant byte or result=1?
+   8 Test: 2's compliment overflow from subtraction of least
+           significant bytes?
+   9 Test: Result less than zero? (Bit 15=1)
+   A Load Condition Code Register from Stack.
+   B Set when interrupt occurs.  If previously set, a NMI is
+        required to exit the wait state.
+   C Set according to the contents of Accumulator A.
+
+  *x SHIFT AND ROTATION DIAGRAMS
+   *1     +-----------------+       C to LSB
+          - C <- 76543210 <-+
+
+   *2    +-----------------+
+         +> C -> 76543210 -+
+
+   *3       C <- 76543210 <- 0(Data)
+               +-+
+
+   *4          ï¿½>76543210 -> C
+
+   *5 (Data)0 -> 76543210 -> C
+
+  FORMULAS
+   1: (Xh/Xl)-(M/M+1)
+   2: Xh=M, Xl=M+1
+   3: SPh=M, SPl=M+1
+   4: M=Xh, M+1=Xl
+*/
+
+
+static void Op__(void)
+{
+       // TRAP is non-maskable, unlike the IRQ... Also, highest priority after
+       // RESET
+       HandleInterrupt(0xFFEE);
+//     regs.cpuFlags |= V63701_STATE_ILLEGAL_INST;
+}
+
+
+//
+// Ok, the exec_op[] array is globally defined here basically to save
+// a LOT of unnecessary typing.  Sure it's ugly, but hey, it works!
+//
+void (* exec_op[256])() = {
+       Op__, Op01, Op__, Op__, Op04, Op05, Op06, Op07, Op08, Op09, Op0A, Op0B, Op0C, Op0D, Op0E, Op0F,
+       Op10, Op11, OpUN, OpUN, Op__, Op__, Op16, Op17, Op18, Op19, Op1A, Op1B, Op__, Op__, Op__, Op__,
+       Op20, Op21, Op22, Op23, Op24, Op25, Op26, Op27, Op28, Op29, Op2A, Op2B, Op2C, Op2D, Op2E, Op2F,
+       Op30, Op31, Op32, Op33, Op34, Op35, Op36, Op37, Op38, Op39, Op3A, Op3B, Op3C, Op3D, Op3E, Op3F,
+       Op40, Op__, Op__, Op43, Op44, Op__, Op46, Op47, Op48, Op49, Op4A, Op__, Op4C, Op4D, Op__, Op4F,
+       Op50, Op__, Op__, Op53, Op54, Op__, Op56, Op57, Op58, Op59, Op5A, Op__, Op5C, Op5D, Op__, Op5F,
+       Op60, Op61, Op62, Op63, Op64, Op65, Op66, Op67, Op68, Op69, Op6A, Op6B, Op6C, Op6D, Op6E, Op6F,
+       Op70, Op71, Op72, Op73, Op74, Op75, Op76, Op77, Op78, Op79, Op7A, Op7B, Op7C, Op7D, Op7E, Op7F,
+       Op80, Op81, Op82, Op83, Op84, Op85, Op86, Op__, Op88, Op89, Op8A, Op8B, Op8C, Op8D, Op8E, Op__,
+       Op90, Op91, Op92, Op93, Op94, Op95, Op96, Op97, Op98, Op99, Op9A, Op9B, Op9C, Op9D, Op9E, Op9F,
+       OpA0, OpA1, OpA2, OpA3, OpA4, OpA5, OpA6, OpA7, OpA8, OpA9, OpAA, OpAB, OpAC, OpAD, OpAE, OpAF,
+       OpB0, OpB1, OpB2, OpB3, OpB4, OpB5, OpB6, OpB7, OpB8, OpB9, OpBA, OpBB, OpBC, OpBD, OpBE, OpBF,
+       OpC0, OpC1, OpC2, OpC3, OpC4, OpC5, OpC6, Op__, OpC8, OpC9, OpCA, OpCB, OpCC, Op__, OpCE, Op__,
+       OpD0, OpD1, OpD2, OpD3, OpD4, OpD5, OpD6, OpD7, OpD8, OpD9, OpDA, OpDB, OpDC, OpDD, OpDE, OpDF,
+       OpE0, OpE1, OpE2, OpE3, OpE4, OpE5, OpE6, OpE7, OpE8, OpE9, OpEA, OpEB, OpEC, OpED, OpEE, OpEF,
+       OpF0, OpF1, OpF2, OpF3, OpF4, OpF5, OpF6, OpF7, OpF8, OpF9, OpFA, OpFB, OpFC, OpFD, OpFE, OpFF
+};
+
+
+//
+// Internal "memcpy" (so we don't have to link with any external libraries!)
+//
+static void myMemcpy(void * dst, void * src, uint32_t size)
+{
+       uint8_t * d = (uint8_t *)dst, * s = (uint8_t *)src;
+
+       for(uint32_t i=0; i<size; i++)
+               d[i] = s[i];
+}
+
+
+#ifdef __DEBUG__
+//int instCount[256];
+//static bool logGo = false;
+static bool logGo = true;
+char instBuf[256];
+extern uint8_t memory[];
+#endif
+//
+// Function to execute 63701 for "cycles" cycles
+//
+void Execute63701(V63701REGS * context, uint32_t cycles)
+{
+//#warning "V63701_STATE_WAI is not properly handled yet! !!! FIX !!!"
+//#warning "Need to convert from destructive clock to non-destructive. !!! FIX !!!"
+       regsPointer = context;
+       myMemcpy(&regs, context, sizeof(V63701REGS));
+       // Explode flags register into individual uint8_ts
+       UNPACK_FLAGS;
+
+#if 0
+       // Execute here...
+       while (regs.clock < cycles)
+#else
+       // Execute here...
+       uint64_t endCycles = regs.clock + (uint64_t)cycles - regs.clockOverrun;
+
+       while (regs.clock < endCycles)
+#endif
+       {
+#ifdef __DEBUG__
+if (logGo)
+{
+       Decode63701(memory, regs.pc, instBuf);
+       WriteLog("%s\n", instBuf);
+}
+#endif
+               if (regs.cpuFlags & V63701_STATE_WAI)
+               {
+                       // Only bail out if no interrupts/resets are pending
+                       if (!(regs.cpuFlags & (V63701_ASSERT_LINE_IRQ | V63701_ASSERT_LINE_NMI | V63701_ASSERT_LINE_RESET | V63701_ASSERT_TIMER_OVERFLOW | V63701_ASSERT_OUTPUT_COMPARE | V63701_ASSERT_INPUT_CAPTURE)))
+                               // Burn any remaining cycles...
+                               regs.clock = endCycles;
+               }
+               else
+               {
+                       uint8_t opcode = regs.RdMem(regs.pc++);
+                       exec_op[opcode]();                                              // Execute that opcode...
+                       regs.clock += CPUCycles[opcode];
+                       uint16_t oldCounter = regs.counter.word;
+                       regs.counter.word += CPUCycles[opcode];
+
+                       // We fake the free running counter above, by adding cycle counts
+                       // to it; this means we can never be sure that the counter will
+                       // be equal to the output compare register. So we have to check
+                       // for two cases: 1st, look for the previous counter value and see
+                       // if we went past it, and 2nd, do the same but check to see if we
+                       // overflowed in the meantime.
+                       if (((oldCounter < regs.outputCompare.word)
+                               && (regs.counter.word >= regs.outputCompare.word))
+                               || ((oldCounter > regs.counter.word)
+                               && (regs.counter.word >= regs.outputCompare.word)))
+                       {
+                               // Set the output compare flag bit
+                               regs.tcsr.bit.ocf = 1;
+
+                               if (regs.tcsr.bit.eoci)
+                               {
+                                       regs.cpuFlags |= V63701_ASSERT_OUTPUT_COMPARE;
+                                       regsPointer->cpuFlags |= V63701_ASSERT_OUTPUT_COMPARE;
+                               }
+                       }
+
+                       // Check for counter overflow
+                       if (regs.counter.word < oldCounter)
+                       {
+                               // Set the timer overflow flag bit
+                               regs.tcsr.bit.tof = 1;
+
+                               if (regs.tcsr.bit.etoi)
+                               {
+                                       regs.cpuFlags |= V63701_ASSERT_TIMER_OVERFLOW;
+                                       regsPointer->cpuFlags |= V63701_ASSERT_TIMER_OVERFLOW;
+                               }
+                       }
+#ifdef __DEBUG__
+if (logGo)
+//     WriteLog(" [PC=%04X, S=%04X, X=%04X, A=%02X, B=%02X, CC=%s%s%s%s%s%s%s%s]\n", regs.pc, regs.s, regs.x, regs.d.acc.a, regs.d.acc.b, (regs.cc & FLAG_E ? "E" : " "), (regs.cc & FLAG_F ? "F" : " "), (regs.cc & FLAG_H ? "H" : " "), (regs.cc & FLAG_I ? "I" : " "), (regs.cc & FLAG_N ? "N" : " "), (regs.cc & FLAG_Z ? "Z" : " "), (regs.cc & FLAG_V ? "V" : " "), (regs.cc & FLAG_C ? "C" : " "));
+       WriteLog(" [PC=%04X S=%04X X=%04X A=%02X B=%02X CC=%s%s%s%s%s%s TCSR=%s%s%s%s%s%s%s%s CT=%04X OC=%04X]\n", regs.pc, regs.s, regs.x, regs.d.acc.a, regs.d.acc.b, (flagH ? "H" : "."), (flagI ? "I" : "."), (flagN ? "N" : "."), (flagZ ? "Z" : "."), (flagV ? "V" : "."), (flagC ? "C" : "."), (regs.tcsr.bit.icf ? "I" :"."), (regs.tcsr.bit.ocf ? "O" :"."), (regs.tcsr.bit.tof ? "T" :"."), (regs.tcsr.bit.eici ? "i" :"."), (regs.tcsr.bit.eoci ? "o" :"."), (regs.tcsr.bit.etoi ? "t" :"."), (regs.tcsr.bit.iedg ? "E" :"."), (regs.tcsr.bit.olvl ? "O" :"."), regs.counter.word, regs.outputCompare.word);
+#endif
+               }
+
+               if (regs.cpuFlags & V63701_ASSERT_LINE_RESET)
+               {
+#ifdef __DEBUG__
+WriteLog("*** RESET LINE ASSERTED ***\n");
+#endif
+                       regs.tcsr.byte = 0;
+                       regs.tcsrWasRead = false;
+                       regs.counter.word = 0;
+                       regs.outputCompare.word = 0xFFFF;
+                       flagI = 1;                                      // Set I
+                       regs.pc = RdMemW(0xFFFE);       // And load PC with the RESET vector
+                       context->cpuFlags = 0;          // Clear all lingering flags...
+                       regs.cpuFlags = 0;
+               }
+               else if (regs.cpuFlags & V63701_ASSERT_LINE_NMI)
+               {
+#ifdef __DEBUG__
+WriteLog("*** NMI LINE ASSERTED ***\n");
+#endif
+                       HandleInterrupt(0xFFFC, V63701_ASSERT_LINE_NMI);
+               }
+               else if (regs.cpuFlags & V63701_ASSERT_LINE_IRQ)
+               {
+#ifdef __DEBUG__
+WriteLog("*** IRQ LINE ASSERTED ***\n");
+#endif
+//                     if (!(regs.cc & FLAG_I))                        // Process an interrupt (I=0)?
+                       if (!flagI)                                                     // Process an interrupt (I=0)?
+                       {
+#ifdef __DEBUG__
+WriteLog("    IRQ TAKEN!\n");
+logGo = true;
+#endif
+                               HandleInterrupt(0xFFF8, V63701_ASSERT_LINE_IRQ);
+                       }
+               }
+               else if (regs.cpuFlags & V63701_ASSERT_INPUT_CAPTURE)
+               {
+#ifdef __DEBUG__
+WriteLog("*** INPUT CAPTURE ASSERTED ***\n");
+#endif
+                       // Process interrupt if no I inhibit set, & enable in TCSR is set
+                       if (!flagI && regs.tcsr.bit.eici)
+                       {
+#ifdef __DEBUG__
+WriteLog("    IC TAKEN!\n");
+logGo = true;
+#endif
+                               HandleInterrupt(0xFFF6, V63701_ASSERT_INPUT_CAPTURE);
+                       }
+               }
+               else if (regs.cpuFlags & V63701_ASSERT_OUTPUT_COMPARE)
+               {
+#ifdef __DEBUG__
+WriteLog("*** OUTPUT COMPARE ASSERTED ***\n");
+#endif
+                       // Process interrupt if no I inhibit set, & enable in TCSR is set
+                       if (!flagI && regs.tcsr.bit.eoci)
+                       {
+#ifdef __DEBUG__
+WriteLog("    OC TAKEN!\n");
+logGo = true;
+#endif
+                               HandleInterrupt(0xFFF4, V63701_ASSERT_OUTPUT_COMPARE);
+                       }
+               }
+               else if (regs.cpuFlags & V63701_ASSERT_TIMER_OVERFLOW)
+               {
+#ifdef __DEBUG__
+WriteLog("*** TIMER OVER ASSERTED ***\n");
+#endif
+                       // Process interrupt if no I inhibit set, & enable in TCSR is set
+                       if (!flagI && regs.tcsr.bit.etoi)
+                       {
+#ifdef __DEBUG__
+WriteLog("    TO TAKEN!\n");
+logGo = true;
+#endif
+                               HandleInterrupt(0xFFF2, V63701_ASSERT_TIMER_OVERFLOW);
+                       }
+               }
+       }
+
+       // If we went longer than the passed in cycles, make a note of it so we can
+       // subtract it out from a subsequent run. It's guaranteed to be positive,
+       // because the condition that exits the main loop above is written such
+       // that regs.clock has to be larger than endCycles to exit from it.
+       regs.clockOverrun = regs.clock - endCycles;
+
+       regs.cc = PACK_FLAGS;                                           // Mash flags back into the CC register
+       myMemcpy(context, &regs, sizeof(V63701REGS));
+}
+
+
+//
+// Get the clock of the currently executing CPU
+//
+uint64_t GetCurrentV63701Clock(void)
+{
+       return regs.clock;
+}
+
+
+static inline void HandleInterrupt(uint16_t vector, uint16_t flag/*= 0*/)
+{
+       if (regs.cpuFlags & V63701_STATE_WAI)
+               regs.clock += 4;
+       else
+       {
+               regs.cc = PACK_FLAGS;   // Mash flags back into the CC register
+               PUSH16(regs.pc);                // Save all regs...
+               PUSH16(regs.x);
+               PUSH(regs.d.acc.a);
+               PUSH(regs.d.acc.b);
+               PUSH(regs.cc);
+               regs.clock += 12;
+       }
+
+       regs.pc = RdMemW(vector);       // And do it!
+       flagI = 1;
+
+       // Clear the passed in flag + WAI state
+       regs.cpuFlags &= ~(flag | V63701_STATE_WAI);
+       regsPointer->cpuFlags &= ~(flag | V63701_STATE_WAI);
+}
+
+
+uint8_t InternalRegisterRead(uint16_t address)
+{
+       switch (address & 0x1F)
+       {
+       case 0x00:
+               return regs.ddr1;
+       case 0x02:
+               return /*(regs.port1read & ~regs.ddr1) |*/ (regs.port1 & regs.ddr1);
+       // Timer Control and Status Register
+       case 0x08:
+               regs.tcsrWasRead = true;
+               return regs.tcsr.byte;
+       // Counter high byte
+       case 0x09:
+               if (regs.tcsrWasRead)
+               {
+                       regs.tcsr.bit.tof = 0;
+                       regs.tcsrWasRead = false;
+               }
+
+               regs.cReadLatch = regs.counter.byte.lo;
+               return regs.counter.byte.hi;
+       // Counter low byte
+       case 0x0A:
+               return regs.cReadLatch;
+       // Output compare high byte
+       case 0x0B:
+               return regs.outputCompare.byte.hi;
+       // Output compare low byte
+       case 0x0C:
+               return regs.outputCompare.byte.lo;
+       // RAM Control register (only bits 6 & 7 are valid)
+       case 0x14:
+               return (regs.ramCtrl & 0xC0) | 0x3F;
+       default:
+#ifdef __DEBUG__
+               printf("V63701: Unhandled register read @ $%02X...\n", address);
+#else
+               ;
+#endif
+       }
+
+       return 0x00;
+}
+
+
+void InternalRegisterWrite(uint16_t address, uint8_t data)
+{
+       switch (address & 0x1F)
+       {
+       case 0x00:
+               regs.ddr1 = data;
+               break;
+       case 0x01:
+               regs.ddr2 = data;
+               break;
+       case 0x02:
+               regs.port1 = data;
+               break;
+       case 0x03:
+               regs.port2 = data;
+               break;
+//     case 0x05:
+       // Timer Control and Status Register
+       case 0x08:
+               // Top 3 bits are RO, so protect them
+               regs.tcsr.byte = (data & 0x1F) | (regs.tcsr.byte & 0xE0);
+               break;
+       // Counter (High Byte)
+       case 0x09:
+               regs.cWriteLatch = data;
+               regs.counter.word = 0xFFF8;
+               break;
+       // Counter (Low Byte)
+       case 0x0A:
+               regs.counter.word = (regs.cWriteLatch << 8) | data;
+               break;
+       // Output Compare Register (High Byte)
+       case 0x0B:
+               regs.outputCompare.byte.hi = data;
+
+               if (regs.tcsrWasRead)
+               {
+                       regs.tcsr.bit.ocf = 0;
+                       regs.tcsrWasRead = false;
+               }
+
+               break;
+       // Output Compare Register (Low Byte)
+       case 0x0C:
+               regs.outputCompare.byte.lo = data;
+
+               if (regs.tcsrWasRead)
+               {
+                       regs.tcsr.bit.ocf = 0;
+                       regs.tcsrWasRead = false;
+               }
+
+               break;
+       // RAM Control register
+       case 0x14:
+               regs.ramCtrl = data;
+               break;
+       default:
+#ifdef __DEBUG__
+               WriteLog("V63701: Unhandled register write @ $%02X...\n", address);
+#else
+               ;
+#endif
+       }
+}
+
diff --git a/src/v63701.h b/src/v63701.h
new file mode 100644 (file)
index 0000000..47c5b18
--- /dev/null
@@ -0,0 +1,132 @@
+//
+// Virtual 63701 Header file
+//
+// by James Hammons
+// (C) 2014 Underground Software
+//
+
+#ifndef __V63701_H__
+#define __V63701_H__
+
+#include <stdint.h>
+
+// Useful defines
+
+#define FLAG_H         0x20            // Half carry
+#define FLAG_I         0x10            // IRQ
+#define FLAG_N         0x08            // Negative
+#define FLAG_Z         0x04            // Zero
+#define FLAG_V         0x02            // oVerflow
+#define FLAG_C         0x01            // Carry
+
+#define V63701_ASSERT_LINE_RESET       0x0001  // v63701 RESET line
+#define V63701_ASSERT_LINE_IRQ         0x0002  // v63701 IRQ line
+#define V63701_ASSERT_LINE_NMI         0x0004  // v63701 NMI line
+#define V63701_STATE_WAI                       0x0008  // v63701 wait for IRQ line
+#define V63701_ASSERT_TIMER_OVERFLOW   0x0010
+#define V63701_ASSERT_OUTPUT_COMPARE   0x0020
+#define V63701_ASSERT_INPUT_CAPTURE    0x0040
+#define V63701_ASSERT_TRAP                     0x0080  // Illegal instruction executed flag
+//#define V6809_START_DEBUG_LOG                0x8000  // Debug log go (temporary!)
+
+// Useful structs
+
+union Byte
+{
+       struct
+       {
+               uint8_t b0: 1; uint8_t b1: 1; uint8_t b2: 1; uint8_t b3: 1;
+               uint8_t b4: 1; uint8_t b5: 1; uint8_t b6: 1; uint8_t b7: 1;
+       } bit;
+       uint8_t byte;
+};
+
+union Word
+{
+       struct
+       {
+               uint8_t lo: 8;
+               uint8_t hi: 8;
+       } byte;
+       uint16_t word;
+};
+
+union Flags
+{
+       struct
+       {
+               uint8_t c: 1;
+               uint8_t v: 1;
+               uint8_t z: 1;
+               uint8_t n: 1;
+               uint8_t i: 1;
+               uint8_t h: 1;
+       } flag;
+       uint8_t byte;
+};
+
+union TCSR
+{
+       struct
+       {
+               uint8_t olvl: 1;
+               uint8_t iedg: 1;
+               uint8_t etoi: 1;        // Enable Timer Over
+               uint8_t eoci: 1;        // Enable Output Compare
+               uint8_t eici: 1;        // Enable Input Capture
+               uint8_t tof: 1;         // Timer Over
+               uint8_t ocf: 1;         // Output Compare
+               uint8_t icf: 1;         // Input Capture
+       } bit;
+       uint8_t byte;
+};
+
+union WordReg
+{
+       struct
+       {
+               uint8_t b: 8;
+               uint8_t a: 8;
+       } acc;
+       uint16_t word;
+};
+
+struct V63701REGS
+{
+       uint16_t pc;                                    // 63701 PC register
+       uint16_t x;                                             // 63701 X index register
+       uint16_t s;                                             // 63701 System stack pointer
+       uint8_t cc;                                             // 63701 Condition Code register
+//     uint8_t a;                                              // 63701 A register
+//     uint8_t b;                                              // 63701 B register
+       WordReg d;                                              // 63701 A & B registers (A is hi, B is lo)
+       TCSR tcsr;                                              // 63701 Timer control/Status register
+       Word counter;                                   // 63701 Counter register
+       Word outputCompare;                             // 63701 Output Compare register
+       uint8_t ddr1;
+       uint8_t ddr2;
+       uint8_t port1;
+       uint8_t port2;
+       uint8_t ramCtrl;
+       uint8_t cWriteLatch;                    // Counter write latch
+       uint8_t cReadLatch;                             // Counter read latch
+       bool tcsrWasRead;                               // TCSR was read flag
+       uint64_t clock;                                 // 63701 clock
+       uint8_t (* RdMem)(uint16_t);    // Address of uint8 read routine
+       void (* WrMem)(uint16_t, uint8_t);      // Address of uint8 write routine
+       uint32_t cpuFlags;                              // v63701 IRQ/RESET flags
+       uint32_t clockOverrun;                  // Amount of overflow between runs
+};
+
+// Function prototypes
+
+void Execute63701(V63701REGS *, uint32_t);     // Function to execute 63701 instructions
+uint64_t GetCurrentV63701Clock(void);          // Get the clock of the currently executing CPU
+
+// It's the responsibility of the user to call these from their R/W handlers.
+// These are usually memory mapped at $00-$1F.
+uint8_t InternalRegisterRead(uint16_t);
+void InternalRegisterWrite(uint16_t, uint8_t);
+
+#endif // __V63701_H__
+
diff --git a/src/ym2151.c b/src/ym2151.c
new file mode 100644 (file)
index 0000000..46776ee
--- /dev/null
@@ -0,0 +1,1437 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <stdint.h>
+//#include "driver.h"
+#include "ym2151.h"
+
+
+// Missing shit (from M.A.M.E.)
+
+#if 1
+#define PI 3.1415629535897932338
+static FILE * errorlog = 0;
+int cpu_scalebyfcount(int);
+void timer_remove(void *);
+void * timer_set(int, int, void (*)(int));
+#endif
+
+
+/*
+**  some globals ...
+*/
+
+/*
+**  Shifts below are subject to change if sampling frequency changes...
+*/
+#define FREQ_SH 16  /* 16.16 fixed point for frequency calculations     */
+#define LFO_SH 24   /*  8.24 fixed point for LFO frequency calculations */
+#define ENV_SH 16   /* 16.16 fixed point for envelope calculations      */
+#define TIMER_SH 16 /* 16.16 fixed point for timers calculations        */
+
+#define ENV_BITS 10
+#define ENV_RES ((int)1<<ENV_BITS)
+#define ENV_STEP (96.0/ENV_RES)
+#define MAX_VOLUME_INDEX ((ENV_RES-1)<<ENV_SH)
+#define MIN_VOLUME_INDEX (0)
+#define VOLUME_OFF (ENV_RES<<ENV_SH)
+
+#define SIN_BITS 10
+#define SIN_LEN ((int)1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define FINAL_SH8 7   /*this shift is applied to final output of all channels to get 8-bit sample */
+#define FINAL_SH16 0  /*this shift is applied to final output of all channels to get 16-bit sample*/
+
+static uint8_t FEED[8] = {0,7,6,5,4,3,2,1}; /*shifts (divider) for output of op.0 which feeds into itself*/
+
+#define TL_TAB_LEN (2*(ENV_RES + ENV_RES + ENV_RES + SIN_LEN))
+static signed int * TL_TAB = NULL;
+/*
+ *  Offset in this table is calculated as:
+ *
+ *  1st ENV_RES:
+ *     TL- main offset (Total attenuation Level), range 0 to 1023 (0-96 dB)
+ *  2nd ENV_RES:
+ *     current envelope value of the operator, range 0 to 1023 (0-96 dB)
+ *  3rd ENV_RES:
+ *     Amplitude Modulation from LFO, range 0 to 1023 (0-96dB)
+ *  4th SIN_LEN:
+ *     Sin Wave Offset from sin_tab, range 0 to about 56 dB only, but lets
+ *     simplify things and assume sin could be 96 dB, range 0 to 1023
+ *
+ *  Length of this table is doubled because we have two separate parts
+ *  for positive and negative halves of sin wave (above and below X axis).
+ */
+
+static signed int * sin_tab[SIN_LEN];  /* sin waveform table in decibel scale */
+
+#if 0
+/*tables below are defined for usage by LFO */
+signed int PMsaw     [SIN_LEN];   /*saw waveform table PM      */
+signed int PMsquare  [SIN_LEN];   /*square waveform table PM   */
+signed int PMtriangle[SIN_LEN];   /*triangle waveform table PM */
+signed int PMnoise   [SIN_LEN];   /*noise waveform table PM    */
+
+uint16_t AMsaw     [SIN_LEN];     /*saw waveform table AM      */
+uint16_t AMsquare  [SIN_LEN];     /*square waveform table AM   */
+uint16_t AMtriangle[SIN_LEN];     /*triangle waveform table AM */
+uint16_t AMnoise   [SIN_LEN];     /*noise waveform table AM    */
+#endif
+
+static int YM2151_CLOCK = 1;           /* this will be passed from 2151intf.c */
+static int YM2151_SAMPFREQ = 1;                /* this will be passed from 2151intf.c */
+//static uint8_t sample_16bit; /* 1 -> 16 bit sample, 0 -> 8 bit */
+
+static int YMBufSize;          /* size of sound buffer, in samples */
+static int YMNumChips;         /* total # of YM's emulated */
+
+static int TimerA[1024];
+static int TimerB[256];
+
+/* ASG 980324: added */
+static double TimerATime[1024];
+static double TimerBTime[256];
+
+static YM2151 * YMPSG = NULL;  /* array of YM's */
+
+signed int * BuffTemp = NULL;  /*temporary buffer for speedup purposes*/
+
+static void (* envelope_calc[5])(OscilRec *);
+static void (* register_writes[256])(uint8_t, uint8_t, uint8_t);
+
+//save output as raw 16-bit sample - just in case you would like to listen to it offline ;-)
+//#define SAVE_SAMPLE
+//#define SAVE_SEPARATE_CHANNELS
+
+#ifdef SAVE_SAMPLE
+#ifdef SAVE_SEPARATE_CHANNELS
+FILE * sample1;
+FILE * sample2;
+FILE * sample3;
+FILE * sample4;
+FILE * sample5;
+FILE * sample6;
+#endif
+FILE * samplesum;
+#endif
+
+int PMTab[8]; /*8 channels */
+/*  this table is used for PM setup of LFO */
+
+int AMTab[8]; /*8 channels */
+/*  this table is used for AM setup of LFO */
+
+static int decib45[16];
+/*decibel table to convert from D1L values to index in lookup table*/
+
+static int attack_curve[ENV_RES];
+
+unsigned int divia[64]; //Attack dividers
+unsigned int divid[64]; //Decay dividers
+static unsigned int A_DELTAS[64+31]; //Attack deltas (64 keycodes + 31 RKS's = 95)
+static unsigned int D_DELTAS[64+31]; //Decay  deltas (64 keycodes + 31 RKS's = 95)
+
+float DT1Tab[32][4]={ /* 8 octaves * 4 key codes, 4 DT1 values */
+/*
+ *  Table defines offset in Hertz from base frequency depending on KC and DT1
+ *  User's Manual page 21
+*/
+/*OCT NOTE DT1=0 DT1=1  DT1=2  DT1=3*/
+/* 0   0*/{0,    0,     0.053, 0.107},
+/* 0   1*/{0,    0,     0.053, 0.107},
+/* 0   2*/{0,    0,     0.053, 0.107},
+/* 0   3*/{0,    0,     0.053, 0.107},
+/* 1   0*/{0,    0.053, 0.107, 0.107},
+/* 1   1*/{0,    0.053, 0.107, 0.160},
+/* 1   2*/{0,    0.053, 0.107, 0.160},
+/* 1   3*/{0,    0.053, 0.107, 0.160},
+/* 2   0*/{0,    0.053, 0.107, 0.213},
+/* 2   1*/{0,    0.053, 0.160, 0.213},
+/* 2   2*/{0,    0.053, 0.160, 0.213},
+/* 2   3*/{0,    0.053, 0.160, 0.267},
+/* 3   0*/{0,    0.107, 0.213, 0.267},
+/* 3   1*/{0,    0.107, 0.213, 0.320},
+/* 3   2*/{0,    0.107, 0.213, 0.320},
+/* 3   3*/{0,    0.107, 0.267, 0.373},
+/* 4   0*/{0,    0.107, 0.267, 0.427},
+/* 4   1*/{0,    0.160, 0.320, 0.427},
+/* 4   2*/{0,    0.160, 0.320, 0.480},
+/* 4   3*/{0,    0.160, 0.373, 0.533},
+/* 5   0*/{0,    0.213, 0.427, 0.587},
+/* 5   1*/{0,    0.213, 0.427, 0.640},
+/* 5   2*/{0,    0.213, 0.480, 0.693},
+/* 5   3*/{0,    0.267, 0.533, 0.747},
+/* 6   0*/{0,    0.267, 0.587, 0.853},
+/* 6   1*/{0,    0.320, 0.640, 0.907},
+/* 6   2*/{0,    0.320, 0.693, 1.013},
+/* 6   3*/{0,    0.373, 0.747, 1.067},
+/* 7   0*/{0,    0.427, 0.853, 1.173},
+/* 7   1*/{0,    0.427, 0.907, 1.173},
+/* 7   2*/{0,    0.480, 1.013, 1.173},
+/* 7   3*/{0,    0.533, 1.067, 1.173}
+};
+
+static uint16_t DT2Tab[4]={ /* 4 DT2 values */
+/*
+ *   DT2 defines offset in cents from base note
+ *
+ *   The table below defines offset in deltas table...
+ *   User's Manual page 22
+ *   Values below were calculated using formula:  value = orig.val / 1.5625
+ *
+ * DT2=0 DT2=1 DT2=2 DT2=3
+ * 0     600   781   950
+ */
+       0,    384,  500,  608
+};
+
+static uint16_t KC_TO_INDEX[16*8];  /*16 note codes * 8 octaves */
+/*translate key code KC (OCT2 OCT1 OCT0 N3 N2 N1 N0) into index in deltas table*/
+
+static signed int DT1deltas[32][8];
+
+static signed int deltas[9*12*64];/*9 octaves, 12 semitones, 64 'cents'  */
+/*deltas in sintable (fixed point) to get the closest frequency possible */
+/*there're 9 octaves because of DT2 (max 950 cents over base frequency)  */
+
+static signed int LFOdeltas[256];  /*frequency deltas for LFO*/
+
+
+void sin_init(void)
+{
+       int x, i;
+       float m;
+
+       for(i=0; i<SIN_LEN; i++)
+       {
+               m = sin(2 * PI * i / SIN_LEN);  /*count sin value*/
+
+               if ((m < 0.0001) && (m > -0.0001)) /*is m very close to zero ?*/
+                       m = ENV_RES - 1;
+               else
+               {
+                       if (m > 0.0)
+                       {
+                               m = 20 * log10(1.0 / m);      /* and how many decibels is it? */
+                               m = m / ENV_STEP;
+                       }
+                       else
+                       {
+                               m = 20 * log10(-1.0 / m);     /* and how many decibels is it? */
+                               m = (m / ENV_STEP) + TL_TAB_LEN / 2;
+                       }
+               }
+
+               sin_tab[i] = &TL_TAB[(unsigned int)m];  /**/
+               //if (errorlog) fprintf(errorlog,"sin %i = %i\n",i,sin_tab[i] );
+       }
+
+       for(x=0; x<TL_TAB_LEN/2; x++)
+       {
+               if (x < ENV_RES)
+               {
+                       if ((x * ENV_STEP) < 6.0)  /*have we passed 6 dB*/
+                       {       /*nope, we didn't */
+                               m = ((1 << 12) - 1) / pow(10, x * ENV_STEP / 20);
+                       }
+                       else
+                       {
+                               /*if yes, we simplify things (and the real chip */
+                               /*probably does it also) and assume that - 6dB   */
+                               /*halves the voltage (it _nearly_ does in fact)  */
+                               m = TL_TAB[(int)((float)x - (6.0 / ENV_STEP))] / 2;
+                       }
+               }
+               else
+               {
+                       m = 0;
+               }
+
+               TL_TAB[        x         ] = m;
+               TL_TAB[x + TL_TAB_LEN / 2] = -m;
+               //if (errorlog) fprintf(errorlog,"tl %04i =%08x\n",x,TL_TAB[x]);
+       }
+
+/* create attack curve */
+       for(i=0; i<ENV_RES; i++)
+       {
+               m = (ENV_RES - 1) / pow(10, i * (48.0 / ENV_RES) / 20);
+               x = m * (1 << ENV_SH);
+               attack_curve[ENV_RES - 1 - i] = x;
+               //if (errorlog) fprintf(errorlog,"attack [%04x] = %08x Volt=%08x\n", ENV_RES-1-i, x/(1<<ENV_SH), TL_TAB[x/(1<<ENV_SH)] );
+       }
+
+       for(x=0; x<16; x++)
+       {
+               i = (x < 15 ? x : x + 16) * (3.0 / ENV_STEP);  /*every 3 dB except for ALL '1' = 45dB+48dB*/
+               i <<= ENV_SH;
+               decib45[x] = i;
+               //if (errorlog) fprintf(errorlog,"decib45[%04x]=%08x\n",x,i );
+       }
+
+#ifdef SAVE_SAMPLE
+#ifdef SAVE_SEPARATE_CHANNELS
+sample1=fopen("samp.raw","wb");
+sample2=fopen("samp2.raw","wb");
+sample3=fopen("samp3.raw","wb");
+sample4=fopen("samp4.raw","wb");
+sample5=fopen("samp5.raw","wb");
+sample6=fopen("samp6.raw","wb");
+#endif
+samplesum=fopen("sampsum.raw","wb");
+#endif
+}
+
+
+void hertz(void)
+{
+       int i, j, oct;
+       long int mult;
+       float pom, pom2, m;
+       float scaler;   /* formula below is true for chip clock=3579545 */
+       /* so we need to scale its output accordingly to the chip clock */
+
+/*this loop calculates Hertz values for notes from c#0 up to c-8*/
+/*including 64 'cents' (100/64 which is 1.5625 of real cent) for each semitone*/
+
+       scaler = (float)YM2151_CLOCK / 3579545.0;
+
+       oct = 768;
+       mult = (long int)1 << FREQ_SH;
+
+       for(i=0; i<1*12*64; i++)
+       {
+               pom = scaler * 6.875 * pow(2, ((i + 4 * 64) * 1.5625 / 1200.0)); /*13.75Hz is note A 12semitones below A-0, so C#0 is 4 semitones above then*/
+               /*calculate phase increment for above precounted Hertz value*/
+               pom2 = ((pom * SIN_LEN) / (float)YM2151_SAMPFREQ) * mult; /*fixed point*/
+
+               deltas[i + oct * 4] = pom2 * 16;  /*oct 4 - center*/
+               deltas[i + oct * 5] = deltas[oct * 4 + i] << 1; //oct 5
+               deltas[i + oct * 6] = deltas[oct * 4 + i] << 2; //oct 6
+               deltas[i + oct * 7] = deltas[oct * 4 + i] << 3; //oct 7
+               deltas[i + oct * 8] = deltas[oct * 4 + i] << 4; //oct 8
+
+               deltas[i + oct * 3] = deltas[oct * 4 + i] >> 1; //oct 3
+               deltas[i + oct * 2] = deltas[oct * 4 + i] >> 2; //oct 2
+               deltas[i + oct * 1] = deltas[oct * 4 + i] >> 3; //oct 1
+               deltas[i + oct * 0] = deltas[oct * 4 + i] >> 4; //oct 0
+
+               //if (errorlog) fprintf(errorlog,"deltas[%04i] = %08x\n",i,deltas[i]);
+       }
+
+       for(j=0; j<4; j++)
+       {
+               for(i=0; i<32; i++)
+               {
+                       pom = scaler * DT1Tab[i][j];
+                       //calculate phase increment for above precounted Hertz value
+                       DT1deltas[i][j] = ((pom * SIN_LEN) / (float)YM2151_SAMPFREQ) * mult; /*fixed point*/
+                       DT1deltas[i][j + 4] = -DT1deltas[i][j];
+               }
+       }
+
+       mult = (long int)1 << LFO_SH;
+       m = (float)YM2151_CLOCK;
+
+       for(i=0; i<256; i++)
+       {
+               j = i & 0x0F;
+               pom = scaler * fabs((m / 65536 / (1 << (i / 16))) - (m / 65536 / 32 / (1 << (i / 16)) * (j + 1)));
+
+               /*calculate phase increment for above precounted Hertz value*/
+               pom2 = ((pom * SIN_LEN) / (float)YM2151_SAMPFREQ) * mult; /*fixed point*/
+               LFOdeltas[0xFF - i] = pom2;
+               //if (errorlog) fprintf(errorlog, "LFO[%02x] = %08x\n",0xff-i, LFOdeltas[0xff-i]);
+       }
+
+       /* calculate KC to index table */
+       j=0;
+       for(i=0; i<16*8; i++)
+       {
+               KC_TO_INDEX[i] = (i >> 4) * 12 * 64 + j * 64 ;
+
+               if ((i & 3) != 3)
+                       j++;    /* change note code */
+
+               if ((i & 15) == 15)
+                       j = 0;  /* new octave */
+
+               //if (errorlog) fprintf(errorlog,"NOTE[%i] = %i\n",i,KC_TO_INDEX[i]);
+       }
+
+       /* precalculate envelope times */
+       for(i=0; i<64; i++)
+       {
+               pom = (16 << (i >> 2)) + (4 << (i >> 2)) * (i & 0x03);
+
+               if ((i >> 2) == 0)
+                       pom = 1; //infinity
+
+               if ((i >> 2) == 15)
+                       pom = 524288; //const
+
+               divid[i] = pom;
+       }
+
+       for(i=0; i<64; i++)
+       {
+               pom = ((128+64+32)<<(i>>2))+((32+16+8)<<(i>>2))*(i&0x03);
+
+               if ((i>>2)==0)  pom=1;    //infinity
+
+               if ((i>>2)==15)
+               {
+                       if ((i & 0x03) == 3)
+                               pom = 153293300; //zero attack time
+                       else
+                               pom = 6422528; //const attack time
+               }
+
+               divia[i] = pom;
+       }
+
+       mult = (long int)1 << ENV_SH;
+
+       for(i=0; i<64; i++)
+       {
+               if (divid[i] == 1)
+                       pom = 0;  //infinity
+               else
+                       pom = (scaler * ENV_RES * mult) / ((float)YM2151_SAMPFREQ * ((float)YM2151_CLOCK / 1000.0 / (float)divid[i]));
+               //if (errorlog) fprintf(errorlog,"i=%03i div=%i time=%f delta=%f\n",i,divid[i],
+               //              (float)YM2151_CLOCK/1000.0/(float)divid[i], pom );
+               D_DELTAS[i] = pom;
+       }
+
+       for (i=0; i<64; i++)
+       {
+               if (divia[i] == 1)
+                       pom = 0;  //infinity
+               else
+                       pom = (scaler * ENV_RES * mult) / ((float)YM2151_SAMPFREQ * ((float)YM2151_CLOCK / 1000.0 / (float)divia[i]));
+
+               //if (errorlog) fprintf(errorlog,"i=%03i div=%i time=%f delta=%f\n",i,divia[i],
+               //              (float)YM2151_CLOCK/1000.0/(float)divia[i], pom );
+               A_DELTAS[i] = pom;
+       }
+
+       // This is only for speedup purposes -> to avoid testing if (keycode+RKS is
+       // over 63)
+       for(i=0; i<32; i++)
+       {
+               D_DELTAS[64 + i] = D_DELTAS[63];
+               A_DELTAS[64 + i] = A_DELTAS[63];
+       }
+
+       /* precalculate timers deltas */
+       /* User's Manual pages 15,16  */
+       mult = (int)1 << TIMER_SH;
+
+       for(i=0; i<1024; i++)
+       {
+               /* ASG 980324: changed to compute both TimerA and TimerATime */
+               pom = (64.0 * (1024.0 - i) / YM2151_CLOCK);
+               TimerATime[i] = pom;
+               TimerA[i] = pom * YM2151_SAMPFREQ * mult;  /*number of samples that timer period should last (fixed point) */
+       }
+
+       for(i=0; i<256; i++)
+       {
+               /* ASG 980324: changed to compute both TimerB and TimerBTime */
+               pom = (1024.0 * (256.0 - i) / YM2151_CLOCK);
+               TimerBTime[i] = pom;
+               TimerB[i] = pom * YM2151_SAMPFREQ * mult;  /*number of samples that timer period should last (fixed point) */
+       }
+}
+
+
+void envelope_attack(OscilRec * op)
+{
+       if ((op->attack_volume -= op->delta_AR) < MIN_VOLUME_INDEX)  //is volume index min already ?
+       {
+               op->volume = MIN_VOLUME_INDEX;
+               op->state++;
+       }
+       else
+               op->volume = attack_curve[op->attack_volume>>ENV_SH];
+}
+
+
+void envelope_decay(OscilRec * op)
+{
+       if ((op->volume += op->delta_D1R) > op->D1L)
+       {
+               //op->volume = op->D1L;
+               op->state++;
+       }
+}
+
+
+void envelope_sustain(OscilRec * op)
+{
+       if ((op->volume += op->delta_D2R) > MAX_VOLUME_INDEX)
+       {
+               op->state = 4;
+               op->volume = VOLUME_OFF;
+       }
+}
+
+
+void envelope_release(OscilRec * op)
+{
+       if ((op->volume += op->delta_RR) > MAX_VOLUME_INDEX)
+       {
+               op->state = 4;
+               op->volume = VOLUME_OFF;
+       }
+}
+
+
+void envelope_nothing(OscilRec *op)
+{
+}
+
+
+inline void envelope_KOFF(OscilRec * op)
+{
+       op->state = 3; /*release*/
+}
+
+
+inline void envelope_KON(OscilRec * op)
+{
+       /*this is to remove the gap time if TL>0*/
+       op->volume = VOLUME_OFF; //(ENV_RES - op->TL)<<ENV_SH; /***  <-  SURE ABOUT IT ?  No, but let's give it a try...*/
+       op->attack_volume = op->volume;
+       op->phase = 0;
+       op->state = 0;    /*KEY ON = attack*/
+       op->OscilFB = 0;  /*Clear feedback after key on */
+}
+
+
+void refresh_chip(YM2151 * PSG)
+{
+       uint16_t kc_index_oscil, kc_index_channel, mul;
+       uint8_t op,v,kc;
+       signed char k,chan;
+       OscilRec * osc;
+
+       for(chan=7; chan>=0; chan--)
+       {
+               kc = PSG->KC[chan];
+               kc_index_channel = KC_TO_INDEX[kc] + PSG->KF[chan];
+               kc >>=2;
+
+               for(k=24; k>=0; k-=8)
+               {
+                       op = chan + k;
+                       osc = &PSG->Oscils[op];
+
+/*calc freq begin*/
+                       v = (PSG->Regs[YM_DT2_D2R_BASE + op] >> 6) & 0x03;   //DT2 value
+                       kc_index_oscil = kc_index_channel + DT2Tab[v]; //DT2 offset
+                       v = PSG->Regs[YM_DT1_MUL_BASE + op];
+                       mul = (v & 0x0F) << 1;
+
+                       if (mul)
+                               osc->freq = deltas[kc_index_oscil] * mul;
+                       else
+                               osc->freq = deltas[kc_index_oscil];
+
+                       osc->freq += DT1deltas[kc][(v >> 4) & 0x07];  //DT1 value
+/*calc freq end*/
+
+/*calc envelopes begin*/
+                       v = kc >> PSG->KS[op];
+                       osc->delta_AR  = A_DELTAS[ osc->AR + v];    /* 2*RR + RKS =max 95*/
+                       osc->delta_D1R = D_DELTAS[osc->D1R + v];    /* 2*RR + RKS =max 95*/
+                       osc->delta_D2R = D_DELTAS[osc->D2R + v];    /* 2*RR + RKS =max 95*/
+                       osc->delta_RR =  D_DELTAS[ osc->RR + v];    /* 2*RR + RKS =max 95*/
+/*calc envelopes end*/
+               }
+       }
+}
+
+
+void write_YM_NON_EMULATED(uint8_t n, uint8_t r, uint8_t v)
+{
+       if (errorlog)
+               fprintf(errorlog, "Write to non emulated register %02x value %02x\n", r, v);
+}
+
+
+void write_YM_KON(uint8_t n, uint8_t r, uint8_t v)
+{
+       uint8_t chan;
+       YM2151 * PSG = &(YMPSG[n]);
+
+       chan = v & 0x07;
+
+       if (v & 0x08)
+               envelope_KON(&PSG->Oscils[chan]);
+       else
+               envelope_KOFF(&PSG->Oscils[chan]);
+
+       if (v & 0x10)
+               envelope_KON(&PSG->Oscils[chan + 16]);
+       else
+               envelope_KOFF(&PSG->Oscils[chan + 16]);
+
+       if (v & 0x20)
+               envelope_KON(&PSG->Oscils[chan + 8]);
+       else
+               envelope_KOFF(&PSG->Oscils[chan + 8]);
+
+       if (v & 0x40)
+               envelope_KON(&PSG->Oscils[chan + 24]);
+       else
+               envelope_KOFF(&PSG->Oscils[chan + 24]);
+}
+
+
+void write_YM_CLOCKA1(uint8_t n, uint8_t r, uint8_t v)
+{
+       YMPSG[n].Regs[r] = v;
+}
+
+
+void write_YM_CLOCKA2(uint8_t n, uint8_t r, uint8_t v)
+{
+       YMPSG[n].Regs[r] = v & 0x03;
+}
+
+
+void write_YM_CLOCKB(uint8_t n, uint8_t r, uint8_t v)
+{
+       YMPSG[n].Regs[r] = v;
+}
+
+
+static void timer_callback_a(int n)
+{
+       YM2151 * PSG = &YMPSG[n];
+//     YM2151UpdateOne(n, cpu_scalebyfcount(YMBufSize));
+
+       if (PSG->handler)
+               (*PSG->handler)();
+
+       PSG->TimA = 0;
+       PSG->TimIRQ |= 1;
+       PSG->TimATimer = 0;
+}
+
+
+static void timer_callback_b(int n)
+{
+       YM2151 * PSG = &YMPSG[n];
+//     YM2151UpdateOne(n, cpu_scalebyfcount(YMBufSize));
+
+       if (PSG->handler)
+               (*PSG->handler)();
+
+       PSG->TimB = 0;
+       PSG->TimIRQ |= 2;
+       PSG->TimBTimer = 0;
+}
+
+
+void write_YM_CLOCKSET(uint8_t n, uint8_t r, uint8_t v)
+{
+       YM2151 * PSG = &(YMPSG[n]);
+
+       v &= 0xBF;
+       //PSG->Regs[r]=v;
+//     if (errorlog) fprintf(errorlog,"CSM= %01x FRESET=%02x, IRQEN=%02x, LOAD=%02x\n",v>>7,(v>>4)&0x03,(v>>2)&0x03,v&0x03 );
+
+       if (v & 0x80) //CSM
+       {}
+
+       /* ASG 980324: remove the timers if they exist */
+       if (PSG->TimATimer)
+               timer_remove(PSG->TimATimer);
+
+       if (PSG->TimBTimer)
+               timer_remove(PSG->TimBTimer);
+
+       PSG->TimATimer = 0;
+       PSG->TimBTimer = 0;
+
+       if (v & 0x01) //LOAD A
+       {
+               PSG->TimA = 1;
+               PSG->TimAVal = TimerA[(PSG->Regs[YM_CLOCKA1] << 2) | PSG->Regs[YM_CLOCKA2]];
+               /* ASG 980324: added a real timer if we have a handler */
+
+               if (PSG->handler)
+                       PSG->TimATimer = timer_set(TimerATime[(PSG->Regs[YM_CLOCKA1] << 2) | PSG->Regs[YM_CLOCKA2]], n, timer_callback_a);
+       }
+       else
+               PSG->TimA = 0;
+
+       if (v & 0x02) //load B
+       {
+               PSG->TimB = 1;
+               PSG->TimBVal = TimerB[PSG->Regs[YM_CLOCKB]];
+
+               /* ASG 980324: added a real timer if we have a handler */
+               if (PSG->handler)
+                       PSG->TimBTimer = timer_set (TimerBTime[PSG->Regs[YM_CLOCKB]], n, timer_callback_b);
+       }
+       else
+               PSG->TimB = 0;
+
+       if (v & 0x04) //IRQEN A
+       {}
+
+       if (v & 0x08) //IRQEN B
+       {}
+
+       if (v & 0x10) //FRESET A
+       {
+               PSG->TimIRQ &= 0xFE;
+       }
+
+       if (v & 0x20) //FRESET B
+       {
+               PSG->TimIRQ &= 0xFD;
+       }
+}
+
+
+void write_YM_CT1_CT2_W(uint8_t n, uint8_t r, uint8_t v)
+{
+       YMPSG[n].Regs[r] = v;
+}
+
+
+void write_YM_CONNECT_BASE(uint8_t n, uint8_t r, uint8_t v)
+{
+       // NOTE: L/R Channel enables are ignored! This emu is mono!
+       YM2151 * PSG = &(YMPSG[n]);
+
+       //PSG->Regs[r] = v;
+       uint8_t chan = r - YM_CONNECT_BASE;
+
+       PSG->ConnectTab[chan] = v & 7; /*connection number*/
+       PSG->FeedBack[chan] = FEED[(v >> 3) & 7];
+}
+
+
+void write_YM_KC_BASE(uint8_t n, uint8_t r, uint8_t v)
+{
+       YMPSG[n].KC[r - YM_KC_BASE] = v;
+       //freq_calc(chan,PSG);
+}
+
+
+void write_YM_KF_BASE(uint8_t n, uint8_t r, uint8_t v)
+{
+       YMPSG[n].KF[r - YM_KF_BASE] = v >> 2;
+       //freq_calc(chan,PSG);
+}
+
+
+void write_YM_PMS_AMS_BASE(uint8_t n, uint8_t r, uint8_t v)
+{
+//     uint8_t chan, i;
+       YM2151 * PSG = &(YMPSG[n]);
+       PSG->Regs[r] = v;
+       uint8_t chan = r - YM_PMS_AMS_BASE;
+
+       PMTab[chan] = v >> 4;  //PMS;
+
+//     if (i && errorlog)
+//             fprintf(errorlog,"PMS CHN %02x =%02x\n", chan, i);
+
+       AMTab[chan] = v & 0x03; //AMS;
+
+//     if (i && errorlog)
+//             fprintf(errorlog,"AMS CHN %02x =%02x\n", chan, i);
+
+}
+
+
+void write_YM_DT1_MUL_BASE(uint8_t n, uint8_t r, uint8_t v)
+{
+       YMPSG[n].Regs[r] = v;
+       //freq_calc(chan,PSG);
+}
+
+
+void write_YM_TL_BASE(uint8_t n, uint8_t r, uint8_t v)
+{
+       v &= 0x7F;
+       YMPSG[n].Oscils[r - YM_TL_BASE].TL =  v << (ENV_BITS - 7); /*7bit TL*/
+}
+
+
+void write_YM_KS_AR_BASE(uint8_t n, uint8_t r, uint8_t v)
+{
+       uint8_t op;
+       YM2151 * PSG;
+
+       op = r - YM_KS_AR_BASE;
+       PSG = &(YMPSG[n]);
+
+       PSG->KS[op] = 3 - (v >> 6);
+       PSG->Oscils[op].AR = (v & 0x1F) << 1;
+}
+
+
+void write_YM_AMS_D1R_BASE(uint8_t n, uint8_t r, uint8_t v)
+{
+       uint8_t op = r - YM_AMS_D1R_BASE;
+
+       if ((v & 0x80) && errorlog)
+               fprintf(errorlog,"AMS ON oper%02x\n", op);
+
+       //HERE something to do with AMS;
+
+       YMPSG[n].Oscils[op].D1R = (v & 0x1F) << 1;
+}
+
+
+void write_YM_DT2_D2R_BASE(uint8_t n, uint8_t r, uint8_t v)
+{
+       YM2151 * PSG = &(YMPSG[n]);
+       OscilRec * osc = &PSG->Oscils[r - YM_DT2_D2R_BASE];
+       PSG->Regs[r] = v;
+
+       osc->D2R = (v & 0x1F) << 1;
+       //freq_calc(chan,PSG);
+}
+
+
+void write_YM_D1L_RR_BASE(uint8_t n, uint8_t r, uint8_t v)
+{
+    OscilRec * osc = &YMPSG[n].Oscils[r - YM_D1L_RR_BASE];
+
+       osc->D1L = decib45[(v >> 4) & 0x0F];
+       osc->RR = ((v & 0x0F) << 2) | 0x02;
+}
+
+
+/*
+** Initialize YM2151 emulator(s).
+**
+** 'num' is the number of virtual YM2151's to allocate
+** 'clock' is the chip clock
+** 'rate' is sampling rate and 'bufsiz' is the size of the
+** buffer that should be updated at each interval
+*/
+int YMInit(int num, int clock, int rate, int sample_bits, int bufsiz, SAMPLE ** buffer)
+{
+       int i;
+
+       if (YMPSG)
+               return (-1);    /* duplicate init. */
+
+       YMNumChips = num;
+       YM2151_SAMPFREQ = rate;
+
+#if 0
+       if (sample_bits == 16)
+               sample_16bit = 1;
+       else
+               sample_16bit = 0;
+#endif
+
+       YM2151_CLOCK = clock;
+       YMBufSize = bufsiz;
+
+       envelope_calc[0] = envelope_attack;
+       envelope_calc[1] = envelope_decay;
+       envelope_calc[2] = envelope_sustain;
+       envelope_calc[3] = envelope_release;
+       envelope_calc[4] = envelope_nothing;
+
+       for(i=0; i<256; i++)
+               register_writes[i] = write_YM_NON_EMULATED;
+
+       register_writes[YM_KON] = write_YM_KON;
+       register_writes[YM_CLOCKA1] = write_YM_CLOCKA1;
+       register_writes[YM_CLOCKA2] = write_YM_CLOCKA2;
+       register_writes[YM_CLOCKB] = write_YM_CLOCKB;
+       register_writes[YM_CLOCKSET] = write_YM_CLOCKSET;
+       register_writes[YM_CT1_CT2_W] = write_YM_CT1_CT2_W;
+
+       for(i=YM_CONNECT_BASE; i<YM_CONNECT_BASE+8; i++)
+               register_writes[i] = write_YM_CONNECT_BASE;
+
+       for(i=YM_KC_BASE; i<YM_KC_BASE+8; i++)
+               register_writes[i] = write_YM_KC_BASE;
+
+       for(i=YM_KF_BASE; i<YM_KF_BASE+8; i++)
+               register_writes[i] = write_YM_KF_BASE;
+
+       for(i=YM_PMS_AMS_BASE; i<YM_PMS_AMS_BASE+8; i++)
+               register_writes[i] = write_YM_PMS_AMS_BASE;
+
+       for(i=YM_DT1_MUL_BASE; i<YM_DT1_MUL_BASE+32; i++)
+               register_writes[i] = write_YM_DT1_MUL_BASE;
+
+       for(i=YM_TL_BASE; i<YM_TL_BASE+32; i++)
+               register_writes[i] = write_YM_TL_BASE;
+
+       for(i=YM_KS_AR_BASE; i<YM_KS_AR_BASE+32; i++)
+               register_writes[i] = write_YM_KS_AR_BASE;
+
+       for(i=YM_AMS_D1R_BASE; i<YM_AMS_D1R_BASE+32; i++)
+               register_writes[i] = write_YM_AMS_D1R_BASE;
+
+       for(i=YM_DT2_D2R_BASE; i<YM_DT2_D2R_BASE+32; i++)
+               register_writes[i] = write_YM_DT2_D2R_BASE;
+
+       for(i=YM_D1L_RR_BASE; i<YM_D1L_RR_BASE+32; i++)
+               register_writes[i] = write_YM_D1L_RR_BASE;
+
+       YMPSG = (YM2151 *)malloc(sizeof(YM2151) * YMNumChips);
+
+       if (YMPSG == NULL)
+               return (1);
+
+       TL_TAB = (signed int *)malloc(sizeof(signed int) * TL_TAB_LEN);
+
+       if (TL_TAB == NULL)
+               return (1);
+
+//     BuffTemp = (signed int *)malloc(sizeof(signed int) * YMBufSize);
+       // 16K ought to be enough for anybody
+       BuffTemp = (signed int *)malloc(sizeof(signed int) * 16384);
+
+       if (BuffTemp == NULL)
+               return (1);
+
+       hertz();
+       sin_init();
+
+       for(i=0; i<YMNumChips; i++)
+       {
+               YMPSG[i].Buf = buffer[i];
+               YMPSG[i].bufp = 0;
+               YMResetChip(i);
+       }
+
+       return 0;
+}
+
+
+void YMShutdown()
+{
+       if (!YMPSG)
+               return;
+
+       free(YMPSG);
+       YMPSG = NULL;
+
+       if (TL_TAB)
+       {
+               free(TL_TAB);
+               TL_TAB = NULL;
+       }
+
+       if (BuffTemp)
+       {
+               free(BuffTemp);
+               BuffTemp = NULL;
+       }
+
+       YM2151_SAMPFREQ = YMBufSize = 0;
+
+#ifdef SAVE_SAMPLE
+#ifdef SAVE_SEPARATE_CHANNELS
+fclose(sample1);
+fclose(sample2);
+fclose(sample3);
+fclose(sample4);
+fclose(sample5);
+fclose(sample6);
+#endif
+fclose(samplesum);
+#endif
+}
+
+
+/* write a register on YM2151 chip number 'n' */
+void YMWriteReg(int n, int r, int v)
+{
+       register_writes[(uint8_t)r]((uint8_t)n, (uint8_t)r, (uint8_t)v);
+}
+
+
+uint8_t YMReadReg(uint8_t n)
+{
+       return YMPSG[n].TimIRQ;
+}
+
+
+/*
+** reset all chip registers.
+*/
+void YMResetChip(int num)
+{
+       int i;
+       YM2151 * PSG = &(YMPSG[num]);
+
+       memset(PSG->Buf, '\0', YMBufSize);
+
+       /* ASG 980324 -- reset the timers before writing to the registers */
+       PSG->TimATimer = 0;
+       PSG->TimBTimer = 0;
+
+       /* initialize hardware registers */
+       for(i=0; i<256; i++)
+               PSG->Regs[i] = 0;
+
+       for(i=0; i<32; i++)
+       {
+               memset(&PSG->Oscils[i], '\0', sizeof(OscilRec));
+               PSG->Oscils[i].volume = VOLUME_OFF;
+               PSG->Oscils[i].state = 4;  //envelope off
+       }
+
+       for(i=0; i<8; i++)
+       {
+               PSG->ConnectTab[i] = 0;
+               PSG->FeedBack[i] = 0;
+       }
+
+       PSG->TimA = 0;
+       PSG->TimB = 0;
+       PSG->TimAVal = 0;
+       PSG->TimBVal = 0;
+       PSG->TimIRQ = 0;
+}
+
+
+static inline signed int op_calc(OscilRec * OP, signed int pm)
+{
+       return sin_tab[(((OP->phase += OP->freq) >> FREQ_SH) + (pm)) & SIN_MASK]
+               [OP->TL + (OP->volume >> ENV_SH)];
+}
+
+
+//void YM2151UpdateOne(int num, int endp)
+void YM2151UpdateOne(void * BUF, int endp)
+{
+//    YM2151 * PSG = &(YMPSG[num]);
+       YM2151 * PSG = &(YMPSG[0]);
+       PSG->bufp = 0;
+//    SAMPLE * BUF;
+       signed int * PSGBUF;
+       OscilRec * OP0, * OP1, * OP2, * OP3;
+       uint16_t i;
+       signed int k, wy;
+#ifdef SAVE_SEPARATE_CHANNELS
+       signed int pom;
+#endif
+
+       refresh_chip(PSG);
+
+       //calculate timers...
+       if (PSG->TimA)
+       {
+               PSG->TimAVal -= ((endp - PSG->bufp) << TIMER_SH);
+
+               if (PSG->TimAVal <= 0)
+               {
+                       PSG->TimA = 0;
+                       PSG->TimIRQ |= 1;
+                       /* ASG 980324 - handled by real timers now
+                       if (PSG->handler !=0) PSG->handler();*/
+               }
+       }
+
+       if (PSG->TimB)
+       {
+               PSG->TimBVal -= ((endp - PSG->bufp) << TIMER_SH);
+
+               if (PSG->TimBVal <= 0)
+               {
+                       PSG->TimB = 0;
+                       PSG->TimIRQ |= 2;
+                       /* ASG 980324 - handled by real timers now
+                       if (PSG->handler !=0) PSG->handler();*/
+               }
+       }
+
+       OP0 = &PSG->Oscils[0     ];
+       OP1 = &PSG->Oscils[0 +  8];
+       OP2 = &PSG->Oscils[0 + 16];
+       OP3 = &PSG->Oscils[0 + 24];
+
+       for(PSGBUF=&BuffTemp[PSG->bufp]; PSGBUF<&BuffTemp[endp]; PSGBUF++)
+       {
+               //chan0
+               envelope_calc[OP0->state](OP0);
+               envelope_calc[OP1->state](OP1);
+               envelope_calc[OP2->state](OP2);
+               envelope_calc[OP3->state](OP3);
+
+               wy = op_calc(OP0, OP0->OscilFB);
+
+               if (PSG->FeedBack[0])
+                       OP0->OscilFB = wy >> PSG->FeedBack[0];
+               else
+                       OP0->OscilFB = 0;
+
+               switch(PSG->ConnectTab[0])
+               {
+               case 0: *(PSGBUF) = op_calc(OP3, op_calc(OP1, op_calc(OP2, wy))); break;
+               case 1: *(PSGBUF) = op_calc(OP3, op_calc(OP1, op_calc(OP2, 0) + wy)); break;
+               case 2: *(PSGBUF) = op_calc(OP3, op_calc(OP1, op_calc(OP2, 0)) + wy); break;
+               case 3: *(PSGBUF) = op_calc(OP3, op_calc(OP2, wy) + op_calc(OP1, 0)); break;
+               case 4: *(PSGBUF) = op_calc(OP2, wy) + op_calc(OP3, op_calc(OP1, 0)); break;
+               case 5: *(PSGBUF) = op_calc(OP2, wy) + op_calc(OP1, wy) + op_calc(OP3, wy); break;
+               case 6: *(PSGBUF) = op_calc(OP2, wy) + op_calc(OP1, 0) + op_calc(OP3, 0); break;
+               default: *(PSGBUF) = wy + op_calc(OP2, 0) + op_calc(OP1, 0) + op_calc(OP3, 0); break;
+               }
+#ifdef SAVE_SEPARATE_CHANNELS
+fputc((uint16_t)(*PSGBUF) & 0xFF, sample1);
+fputc(((uint16_t)(*PSGBUF) >> 8) & 0xFF, sample1);
+#endif
+       }
+
+       //chan1
+       OP0 = &PSG->Oscils[1     ];
+       OP1 = &PSG->Oscils[1 +  8];
+       OP2 = &PSG->Oscils[1 + 16];
+       OP3 = &PSG->Oscils[1 + 24];
+
+       for(PSGBUF=&BuffTemp[PSG->bufp]; PSGBUF<&BuffTemp[endp]; PSGBUF++)
+       {
+               envelope_calc[OP0->state](OP0);
+               envelope_calc[OP1->state](OP1);
+               envelope_calc[OP2->state](OP2);
+               envelope_calc[OP3->state](OP3);
+
+               wy = op_calc(OP0, OP0->OscilFB);
+
+               if (PSG->FeedBack[1])
+                               OP0->OscilFB = wy >> PSG->FeedBack[1];
+               else
+                               OP0->OscilFB = 0;
+
+#ifdef SAVE_SEPARATE_CHANNELS
+pom = *(PSGBUF);
+#endif
+               switch(PSG->ConnectTab[1])
+               {
+               case 0: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,wy) ) );   break;
+               case 1: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)+wy ) ); break;
+               case 2: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)) +wy ); break;
+               case 3: *(PSGBUF) += op_calc(OP3, op_calc(OP2, wy)+op_calc(OP1,0) );  break;
+               case 4: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP3, op_calc(OP1,0));  break;
+               case 5: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1, wy) + op_calc(OP3,wy); break;
+               case 6: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1,0) + op_calc(OP3,0);    break;
+               default: *(PSGBUF) += wy + op_calc(OP2, 0) + op_calc(OP1, 0) + op_calc(OP3, 0); break;
+               }
+
+#ifdef SAVE_SEPARATE_CHANNELS
+fputc((uint16_t)((*PSGBUF)-pom)&0xff,sample2);
+fputc(((uint16_t)((*PSGBUF)-pom)>>8)&0xff,sample2);
+#endif
+       }
+
+//chan2
+       OP0 = &PSG->Oscils[2     ];
+       OP1 = &PSG->Oscils[2 +  8];
+       OP2 = &PSG->Oscils[2 + 16];
+       OP3 = &PSG->Oscils[2 + 24];
+
+for( PSGBUF = &BuffTemp[PSG->bufp]; PSGBUF < &BuffTemp[endp]; PSGBUF++ )
+{
+       envelope_calc[OP0->state](OP0);
+       envelope_calc[OP1->state](OP1);
+       envelope_calc[OP2->state](OP2);
+       envelope_calc[OP3->state](OP3);
+
+       wy = op_calc(OP0, OP0->OscilFB);
+       if (PSG->FeedBack[2])
+                       OP0->OscilFB = wy >> PSG->FeedBack[2];
+       else
+                       OP0->OscilFB = 0;
+
+#ifdef SAVE_SEPARATE_CHANNELS
+       pom=*(PSGBUF);
+#endif
+       switch(PSG->ConnectTab[2])
+       {
+       case 0: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,wy) ) );   break;
+       case 1: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)+wy ) ); break;
+       case 2: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)) +wy ); break;
+       case 3: *(PSGBUF) += op_calc(OP3, op_calc(OP2, wy)+op_calc(OP1,0) );  break;
+       case 4: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP3, op_calc(OP1,0));  break;
+       case 5: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1, wy) + op_calc(OP3,wy); break;
+       case 6: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1,0) + op_calc(OP3,0);    break;
+       default:*(PSGBUF) += wy + op_calc(OP2,0) + op_calc(OP1,0) + op_calc(OP3,0);break;
+       }
+#ifdef SAVE_SEPARATE_CHANNELS
+fputc((uint16_t)((*PSGBUF)-pom)&0xff,sample3);
+fputc(((uint16_t)((*PSGBUF)-pom)>>8)&0xff,sample3);
+#endif
+}
+
+//chan3
+       OP0 = &PSG->Oscils[3     ];
+       OP1 = &PSG->Oscils[3 +  8];
+       OP2 = &PSG->Oscils[3 + 16];
+       OP3 = &PSG->Oscils[3 + 24];
+
+for( PSGBUF = &BuffTemp[PSG->bufp]; PSGBUF < &BuffTemp[endp]; PSGBUF++ )
+{
+       envelope_calc[OP0->state](OP0);
+       envelope_calc[OP1->state](OP1);
+       envelope_calc[OP2->state](OP2);
+       envelope_calc[OP3->state](OP3);
+
+       wy = op_calc(OP0, OP0->OscilFB);
+       if (PSG->FeedBack[3])
+                       OP0->OscilFB = wy >> PSG->FeedBack[3];
+       else
+                       OP0->OscilFB = 0;
+
+#ifdef SAVE_SEPARATE_CHANNELS
+       pom=*(PSGBUF);
+#endif
+       switch(PSG->ConnectTab[3])
+       {
+       case 0: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,wy) ) );   break;
+       case 1: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)+wy ) ); break;
+       case 2: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)) +wy ); break;
+       case 3: *(PSGBUF) += op_calc(OP3, op_calc(OP2, wy)+op_calc(OP1,0) );  break;
+       case 4: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP3, op_calc(OP1,0));  break;
+       case 5: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1, wy) + op_calc(OP3,wy); break;
+       case 6: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1,0) + op_calc(OP3,0);    break;
+       default:*(PSGBUF) += wy + op_calc(OP2,0) + op_calc(OP1,0) + op_calc(OP3,0);break;
+       }
+#ifdef SAVE_SEPARATE_CHANNELS
+fputc((uint16_t)((*PSGBUF)-pom)&0xff,sample4);
+fputc(((uint16_t)((*PSGBUF)-pom)>>8)&0xff,sample4);
+#endif
+}
+
+       //chan4
+       OP0 = &PSG->Oscils[4     ];
+       OP1 = &PSG->Oscils[4 +  8];
+       OP2 = &PSG->Oscils[4 + 16];
+       OP3 = &PSG->Oscils[4 + 24];
+
+       for(PSGBUF=&BuffTemp[PSG->bufp]; PSGBUF<&BuffTemp[endp]; PSGBUF++)
+       {
+               envelope_calc[OP0->state](OP0);
+               envelope_calc[OP1->state](OP1);
+               envelope_calc[OP2->state](OP2);
+               envelope_calc[OP3->state](OP3);
+
+               wy = op_calc(OP0, OP0->OscilFB);
+
+               if (PSG->FeedBack[4])
+                       OP0->OscilFB = wy >> PSG->FeedBack[4];
+               else
+                       OP0->OscilFB = 0;
+
+       #ifdef SAVE_SEPARATE_CHANNELS
+               pom = *(PSGBUF);
+       #endif
+
+               switch(PSG->ConnectTab[4])
+               {
+               case 0: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,wy) ) );   break;
+               case 1: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)+wy ) ); break;
+               case 2: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)) +wy ); break;
+               case 3: *(PSGBUF) += op_calc(OP3, op_calc(OP2, wy)+op_calc(OP1,0) );  break;
+               case 4: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP3, op_calc(OP1,0));  break;
+               case 5: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1, wy) + op_calc(OP3,wy); break;
+               case 6: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1,0) + op_calc(OP3,0);    break;
+               default:*(PSGBUF) += wy + op_calc(OP2,0) + op_calc(OP1,0) + op_calc(OP3,0);break;
+               }
+
+#ifdef SAVE_SEPARATE_CHANNELS
+fputc((uint16_t)((*PSGBUF)-pom)&0xff,sample5);
+fputc(((uint16_t)((*PSGBUF)-pom)>>8)&0xff,sample5);
+#endif
+       }
+
+       //chan5
+       OP0 = &PSG->Oscils[5     ];
+       OP1 = &PSG->Oscils[5 +  8];
+       OP2 = &PSG->Oscils[5 + 16];
+       OP3 = &PSG->Oscils[5 + 24];
+
+for( PSGBUF = &BuffTemp[PSG->bufp]; PSGBUF < &BuffTemp[endp]; PSGBUF++ )
+{
+       envelope_calc[OP0->state](OP0);
+       envelope_calc[OP1->state](OP1);
+       envelope_calc[OP2->state](OP2);
+       envelope_calc[OP3->state](OP3);
+
+       wy = op_calc(OP0, OP0->OscilFB);
+       if (PSG->FeedBack[5])
+                       OP0->OscilFB = wy >> PSG->FeedBack[5];
+       else
+                       OP0->OscilFB = 0;
+
+#ifdef SAVE_SEPARATE_CHANNELS
+       pom=*(PSGBUF);
+#endif
+       switch(PSG->ConnectTab[5])
+       {
+       case 0: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,wy) ) );   break;
+       case 1: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)+wy ) ); break;
+       case 2: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)) +wy ); break;
+       case 3: *(PSGBUF) += op_calc(OP3, op_calc(OP2, wy)+op_calc(OP1,0) );  break;
+       case 4: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP3, op_calc(OP1,0));  break;
+       case 5: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1, wy) + op_calc(OP3,wy); break;
+       case 6: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1,0) + op_calc(OP3,0);    break;
+       default:*(PSGBUF) += wy + op_calc(OP2,0) + op_calc(OP1,0) + op_calc(OP3,0);break;
+       }
+#ifdef SAVE_SEPARATE_CHANNELS
+fputc((uint16_t)((*PSGBUF)-pom)&0xff,sample6);
+fputc(((uint16_t)((*PSGBUF)-pom)>>8)&0xff,sample6);
+#endif
+}
+
+//chan6
+       OP0 = &PSG->Oscils[6     ];
+       OP1 = &PSG->Oscils[6 +  8];
+       OP2 = &PSG->Oscils[6 + 16];
+       OP3 = &PSG->Oscils[6 + 24];
+
+for( PSGBUF = &BuffTemp[PSG->bufp]; PSGBUF < &BuffTemp[endp]; PSGBUF++ )
+{
+       envelope_calc[OP0->state](OP0);
+       envelope_calc[OP1->state](OP1);
+       envelope_calc[OP2->state](OP2);
+       envelope_calc[OP3->state](OP3);
+
+       wy = op_calc(OP0, OP0->OscilFB);
+       if (PSG->FeedBack[6])
+                       OP0->OscilFB = wy >> PSG->FeedBack[6];
+       else
+                       OP0->OscilFB = 0;
+
+       switch(PSG->ConnectTab[6])
+       {
+       case 0: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,wy) ) );   break;
+       case 1: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)+wy ) ); break;
+       case 2: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)) +wy ); break;
+       case 3: *(PSGBUF) += op_calc(OP3, op_calc(OP2, wy)+op_calc(OP1,0) );  break;
+       case 4: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP3, op_calc(OP1,0));  break;
+       case 5: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1, wy) + op_calc(OP3,wy); break;
+       case 6: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1,0) + op_calc(OP3,0);    break;
+       default:*(PSGBUF) += wy + op_calc(OP2,0) + op_calc(OP1,0) + op_calc(OP3,0);break;
+       }
+}
+
+       //chan7
+       OP0 = &PSG->Oscils[7     ];
+       OP1 = &PSG->Oscils[7 +  8];
+       OP2 = &PSG->Oscils[7 + 16];
+       OP3 = &PSG->Oscils[7 + 24];
+
+       for(PSGBUF=&BuffTemp[PSG->bufp]; PSGBUF<&BuffTemp[endp]; PSGBUF++)
+       {
+               envelope_calc[OP0->state](OP0);
+               envelope_calc[OP1->state](OP1);
+               envelope_calc[OP2->state](OP2);
+               envelope_calc[OP3->state](OP3);
+
+               wy = op_calc(OP0, OP0->OscilFB);
+
+               if (PSG->FeedBack[7])
+                       OP0->OscilFB = wy >> PSG->FeedBack[7];
+               else
+                       OP0->OscilFB = 0;
+
+               switch(PSG->ConnectTab[7])
+               {
+               case 0: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,wy) ) );   break;
+               case 1: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)+wy ) ); break;
+               case 2: *(PSGBUF) += op_calc(OP3, op_calc(OP1, op_calc(OP2,0)) +wy ); break;
+               case 3: *(PSGBUF) += op_calc(OP3, op_calc(OP2, wy)+op_calc(OP1,0) );  break;
+               case 4: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP3, op_calc(OP1,0));  break;
+               case 5: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1, wy) + op_calc(OP3,wy); break;
+               case 6: *(PSGBUF) += op_calc(OP2,wy) + op_calc(OP1,0) + op_calc(OP3,0);    break;
+               default:*(PSGBUF) += wy + op_calc(OP2,0) + op_calc(OP1,0) + op_calc(OP3,0);break;
+               }
+       }
+
+//     BUF = PSG->Buf;
+       PSGBUF = &BuffTemp[PSG->bufp];
+
+       for(i=PSG->bufp; i<endp; i++)
+       {
+               k = *(PSGBUF++);
+
+#ifdef SAVE_SAMPLE
+fputc((uint16_t)(-k) & 0xFF, samplesum);
+fputc(((uint16_t)(-k) >> 8) & 0xFF, samplesum);
+#endif
+
+//             if (sample_16bit)
+               {
+                       /*16 bit mode*/
+                       k >>= FINAL_SH16;  //AUDIO_CONV
+                       k <<= 2;
+
+                       if (k > 32767)
+                               k = 32767;
+                       else if (k < -32768)
+                               k = -32768;
+
+                       ((uint16_t *)BUF)[i] = (uint16_t)k;
+               }
+#if 0
+               else
+               {
+                       /*8 bit mode*/
+                       k >>= FINAL_SH8;  //AUDIO_CONV
+
+                       if (k > 127)
+                               k = 127;
+                       else if (k < -128)
+                               k = -128;
+
+                       ((uint8_t *)BUF)[i] = (uint8_t)k;
+               }
+#endif
+       }
+
+       PSG->bufp = endp;
+}
+
+
+/*
+** called to update all chips
+*/
+void YM2151Update(void)
+{
+       int i;
+       for(i=0; i<YMNumChips; i++)
+       {
+               if (YMPSG[i].bufp < YMBufSize)
+;//                    YM2151UpdateOne(i, YMBufSize);
+
+               YMPSG[i].bufp = 0;
+       }
+}
+
+
+/*
+** return the buffer into which YM2151Update() has just written it's sample
+** data
+*/
+SAMPLE * YMBuffer(int n)
+{
+       return YMPSG[n].Buf;
+}
+
+
+void YMSetIrqHandler(int n, void(* handler)(void))
+{
+       YMPSG[n].handler = handler;
+}
+
diff --git a/src/ym2151.h b/src/ym2151.h
new file mode 100644 (file)
index 0000000..b1719a9
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+**
+** File: ym2151.h -- header file for software implementation of YM-2151
+**                                             FM Operator Type-M(OPM).
+**
+** (C) Jarek Burczynski (s0246@goblin.pjwstk.waw.pl) September, 1st 1997
+**
+** First beta release: December, 16th 1997
+** Version 1.0 beta release: March, 16th 1998
+**
+**
+** CAUTION !!! CAUTION !!!           H E L P   W A N T E D !!!
+**
+** If you have some very technical information about YM-2151, or possibly you
+** own a computer (like X68000) which has YM-2151 built-in (so you could make
+** a few samples of real chip), and you would like to help:
+**                  - PLEASE CONTACT ME ASAP!!! -
+**
+**
+**
+** I would like to thank to Shigeharu Isoda without whom this project wouldn't
+** be possible to finish (possibly even start). He has been so kind and helped
+** me by scanning his YM2151 Japanese Manual first, and then by answering MANY
+** of questions I've asked (I can't read Japanese still, although I study
+** informatics at Polish-Japanese Institute here in Poland).
+**
+** I would like to thank to nao (nao@ms6.hinet.net) also - he has been the one
+** who gave me some information about YM2151, and pointed me to Shigeharu.
+**
+** Big thanks to Aaron Giles and Chris Hardy - they've made some samples of one
+** of my favourite arcade games so I could compare it to my emulator. That's
+** how I know there's still something I need to know about this chip.
+**
+** Greetings to Ishmair (thanks for the datasheet !) who has inspired me to
+** write my own 2151 emu by telling me "... maybe with some somes and a lot of
+** luck ... in theory it's difficult ... " - wow that really was a challenge :)
+**
+*/
+
+#ifndef __YM2151_H__
+#define __YM2151_H__
+
+// Register IDs
+#define YM_KON         (0x08)
+#define YM_NOISE       (0x0F)
+
+#define YM_CLOCKA1     (0x10)
+#define YM_CLOCKA2     (0x11)
+#define YM_CLOCKB      (0x12)
+#define YM_CLOCKSET    (0x14)
+
+#define YM_LFRQ                (0x18)
+#define YM_PMD_AMD     (0x19)
+#define YM_CT1_CT2_W   (0x1B)
+
+
+#define YM_CONNECT_BASE        (0x20) /*1 reg per FM channel*/
+#define YM_KC_BASE     (0x28)
+#define YM_KF_BASE     (0x30)
+#define YM_PMS_AMS_BASE        (0x38)
+
+#define YM_DT1_MUL_BASE        (0x40) /*1 reg per operator*/
+#define YM_TL_BASE     (0x60)
+#define YM_KS_AR_BASE  (0x80)
+#define YM_AMS_D1R_BASE        (0xA0)
+#define YM_DT2_D2R_BASE        (0xC0)
+#define YM_D1L_RR_BASE (0xE0)
+
+typedef void SAMPLE;  /*Yep, 16 bit is supported, at last*/
+
+typedef struct{      /*oscillator data */
+       signed int freq;                /*oscillator frequency (in indexes per sample) (fixed point)*/
+       signed int phase;               /*accumulated oscillator phase (in indexes)    (fixed point)*/
+
+       unsigned int TL;                /*this is output level, in fact     */
+       signed int volume;              /*oscillator volume (fixed point)   */
+        signed int attack_volume;      /*used for attack phase calculations*/
+
+       signed int OscilFB;             /*oscillator self feedback used only by operators 0*/
+
+       int D1L;
+       unsigned char AR;
+       unsigned char D1R;
+       unsigned char D2R;
+       unsigned char RR;
+       signed int delta_AR;    /*volume delta for attack  phase (fixed point)*/
+       signed int delta_D1R;   /*volume delta for decay   phase (fixed point)*/
+       signed int delta_D2R;   /*volume delta for sustain phase (fixed point)*/
+       signed int delta_RR;    /*volume delta for release phase (fixed point)*/
+       unsigned char state;    /*Envelope state: 0-attack(AR) 1-decay(D1R) 2-sustain(D2R) 3-release(RR) 4-finished*/
+} OscilRec;
+
+
+/* here's the virtual YM2151 */
+typedef struct ym2151_f {
+    SAMPLE * Buf;      /* sound buffer */
+    int bufp;
+    OscilRec Oscils[32];         /*there are 32 operators in YM2151*/
+    unsigned char Regs[256];     /*table of internal chip registers*/
+    unsigned char FeedBack[8];   /*feedback shift value for operators 0 in each channel*/
+    unsigned char ConnectTab[8]; /*connection algorithm number for each channel*/
+    unsigned char KC[8];       /*KC for each channel*/
+    unsigned char KF[32];       /*KF for each operator*/
+    unsigned char KS[32];      /*KS for each operator*/
+
+    unsigned char TimA, TimB, TimIRQ;
+    signed int TimAVal, TimBVal;
+    void * TimATimer, * TimBTimer;  /* ASG 980324 -- added for tracking timers */
+    void (* handler)(void);
+} YM2151;
+
+
+/*
+** Initialize YM2151 emulator(s).
+**
+** 'num' is the number of virtual YM2151's to allocate
+** 'rate' is sampling rate and 'bufsiz' is the size of the
+** buffer that should be updated at each interval
+*/
+int YMInit(int num, int clock, int rate, int sample_bits, int bufsiz, SAMPLE ** buffer);
+
+/*
+** shutdown the YM2151 emulators ...
+*/
+void YMShutdown(void);
+
+/*
+** reset all chip registers for YM2151 number 'num'
+*/
+void YMResetChip(int num);
+
+/*
+** called to update all chips; should be called about 50 - 70 times per
+** second ... (depends on sample rate and buffer size)
+*/
+void YM2151Update(void);
+
+//void YM2151UpdateOne(int num, int endp);
+void YM2151UpdateOne(void * BUF, int endp);
+
+/*
+** write 'v' to register 'r' on YM2151 chip number 'n'
+*/
+void YMWriteReg(int n, int r, int v);
+
+/*
+** read status register on YM2151 chip number 'n'
+*/
+unsigned char YMReadReg(unsigned char n);
+
+
+/*
+** return pointer to synthesized data
+*/
+SAMPLE * YMBuffer(int n);
+
+void YMSetIrqHandler(int n, void (* handler)(void));
+
+#endif /* _H_YM2151_ */
+
diff --git a/test/mcu-sound.cpp b/test/mcu-sound.cpp
new file mode 100644 (file)
index 0000000..6c8f230
--- /dev/null
@@ -0,0 +1,557 @@
+//
+// Rolling Thunder HD63701/YM2151 sound test
+//
+// by James Hammons
+// (C) 2014 Underground Software
+//
+
+#include <stdio.h>
+#include <SDL2/SDL.h>
+#include "v63701.h"
+#include "ym2151.h"
+#include "dis63701.h"
+
+
+// HD63701's memory (RAM & ROM)
+uint8_t memory[0x10000];
+
+// Sound vars
+static SDL_AudioSpec desired, obtained;
+static SDL_AudioDeviceID device;
+static bool soundInitialized = false;
+static int16_t sample;
+static uint8_t soundBuffer[1024];
+
+// Other vars
+static uint32_t frameCount = 0;
+static uint32_t startTicks;
+static int ymRegister;
+
+// Do this, for now
+#define WriteLog printf
+#define SAMPLE_RATE    48000
+
+// Private function prototypes
+static void SDLSoundCallback(void * userdata, Uint8 * buffer, int length);
+
+
+// Bogus M.A.M.E. shite
+int cpu_scalebyfcount(int f) { return f; }
+void timer_remove(void *) { printf("STUB: timer_remove()\n"); }
+void * timer_set(int, int, void (*)(int)) { printf("STUB: timer_set()\n"); return 0; }
+
+
+uint8_t ReadMemory(uint16_t address)
+{
+#if 1
+       if (address < 0x20)
+       {
+//             printf("V63701 read $%02X from $%02X...\n", memory[address], address);
+               return InternalRegisterRead(address);
+       }
+#endif
+
+       if ((address >= 0x2000) && (address <= 0x2001))
+       {
+               return YMReadReg(0);
+       }
+//     else if (address == 0x2020)
+//             return input_port_0_r(0);
+//     else if (address == 0x2021)
+//             return input_port_1_r(0);
+
+#if 0
+       // This is a cheap hack to fool the MCU into thinking something's
+       // attached to it...
+       if (address == 0x1181)
+               return 0xA6;
+#endif
+
+       return memory[address];
+}
+
+
+void WriteMemory(uint16_t address, uint8_t data)
+{
+#if 1
+       if (address < 0x20)
+       {
+//             printf("V63701 wrote $%02X to $%02X...\n", data, address);
+               InternalRegisterWrite(address, data);
+               return;
+       }
+#endif
+
+       if (((address >= 0x4000) && (address <= 0xBFFF))
+               || (address >= 0xF000))
+               return;
+       else if (address == 0x2000)
+       {
+               ymRegister = data;
+               return;
+       }
+       else if (address == 0x2001)
+       {
+//printf("Writing $%02X to YM2151 register $%02X...\n", data, ymRegister);
+               YMWriteReg(0, ymRegister, data);
+               return;
+       }
+
+       // RAM is from $0 - $3FFF, $C000 - $EFFF
+       memory[address] = data;
+}
+
+
+bool LoadMemory(void)
+{
+       FILE * file1 = fopen("../ROMs/rt1-mcu.bin", "rb");
+       FILE * file2 = fopen("../ROMs/rt1-4.6b", "rb");
+
+       if (!file1)
+       {
+               printf("Could not open file \"rt1-mcu.bin\"!\n");
+               return false;
+       }
+
+       if (!file2)
+       {
+               printf("Could not open file \"rt1-4.6b\"!\n");
+               return false;
+       }
+
+       fread(&memory[0xF000], 1, 0x1000, file1);
+       fread(&memory[0x4000], 1, 0x8000, file2);
+       fclose(file1);
+       fclose(file2);
+
+       return true;
+}
+
+
+void InitSound(void)
+{
+       SDL_zero(desired);
+       desired.freq = SAMPLE_RATE;             // SDL will do conversion on the fly, if it can't get the exact rate. Nice!
+       desired.format = AUDIO_S16SYS;  // This uses the native endian (for portability)...
+       desired.channels = 1;
+       desired.samples = 512;                  // Let's try a 1/2K buffer (can always go lower)
+       desired.callback = SDLSoundCallback;
+
+       device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, 0);
+
+       if (device == 0)
+       {
+               WriteLog("Sound: Failed to initialize SDL sound.\n");
+               return;
+       }
+
+//     conditional = SDL_CreateCond();
+//     mutex = SDL_CreateMutex();
+//     mutex2 = SDL_CreateMutex();// Let's try real signalling...
+//     soundBufferPos = 0;
+//     lastToggleCycles = 0;
+       sample = desired.silence;       // ? wilwok ? yes
+
+       SDL_PauseAudioDevice(device, 0);                        // Start playback!
+       soundInitialized = true;
+       WriteLog("Sound: Successfully initialized.\n");
+}
+
+
+static void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
+{
+#if 0
+//printf("Callback: buffer size is %i bytes.\n", length);
+       do
+       {
+               YM2151Update();
+
+               if (length >= 1024)
+               {
+                       memcpy(buffer, soundBuffer, 1024);
+                       buffer += 1024;
+                       length -= 1024;
+               }
+               else
+               {
+                       memcpy(buffer, soundBuffer, length);
+                       length = 0;
+               }
+       }
+       while (length > 0);
+#else
+       // Length / 2 is because it's set up for 16-bit samples
+       YM2151UpdateOne(buffer, length / 2);
+#endif
+}
+
+
+void ShutdownSound(void)
+{
+       if (soundInitialized)
+       {
+               SDL_PauseAudioDevice(device, 1);
+               SDL_CloseAudioDevice(device);
+//             SDL_DestroyCond(conditional);
+//             SDL_DestroyMutex(mutex);
+//             SDL_DestroyMutex(mutex2);
+               WriteLog("Sound: Done.\n");
+       }
+}
+
+
+#if 0
+/*
+YM2151 registers:
+
+WRITE
+-----
+01: Test
+08: (SM)^KON   1(CH #)  (Key on, SN=4 bits, CH #=3 (SN = C2,M2,C1,M1))
+0F: [NE] [XX] [NFRQ][][][][] (Noise enable, noise frequency)
+10: CLKA1
+11: [XXXXXX][CLKA2][]
+12: CLKB
+14: [CSM][X][FLAG RESET B][A][[IRQEN B][A][LOAD B][A]
+18: LFRQ (LFO frequency)
+19: PMD/AMD (7 bits, bit 8 => 1=PMD, 0=AMD)
+1B: [CT][][XXXX][W][] (Ctrl Output,Wave => 0=saw, 1=square, 2=triangle, 3=noise)
+
+20-3F: 
+    [RL][][FB][][][CONECT][][] (Right/Left, Feedback, Connection)
+    [X][KC][][][][][][] (Key code, 7 bits: 3=octave, 4=note)
+    [KF][][][][][][XX] (Key fractionb, 6-bits)
+    [X][PMS][][][XX][AMS][] 
+40-5F:
+    [X][DT1][][][MUL][][][] (Detune 1, Phase multiply)
+60-7F:
+    [X][TL][][][][][][] (Total level, 7 bits)
+80-9F:
+    [KS][][X][AR][][][][] (Key scaling, Attack rate)
+A0-BF:
+    [AMS-EN][XX][D1R][][][][] (1st decay rate)
+C0-DF:
+    [DT2][][X][D2R][][][][] (Detune 2, 2nd decay rate)
+E0-FF:
+    [D1L][][][][RR][][][] (1st decay level, Release rate)
+
+READ
+----
+XX: [B][XXXXX][ist][] (Busy flag, 0=ready, 1=busy; IRQ status)
+
+
+FUNCTION | Modulator 1     | Modulator 2            | Carrier 1       2  |
+---------+-----------------+------------------------+------------ ... ---|
+Slot #   | 1 2 3 4 5 6 7 8 | 9 10 11 12 13 14 15 16 | 17 18 19 20 ... 32 |
+Channel #| 1 2 3 4 5 6 7 8 | 1  2  3  4  5  6  7 8  |  1  2  3  4 ...  8 |
+
+KEY CODE:
+NOTE: C# D  D# E  F  F# G  G# A A# B  C
+       0 1  2  4  5  6  8  9 10 12 13 14
+*/
+#endif
+
+
+int main(int, char * [])
+{
+       V63701REGS mcu;
+       memset(&mcu, 0, sizeof(V63701REGS));
+       mcu.RdMem = ReadMemory;
+       mcu.WrMem = WriteMemory;
+
+       mcu.cpuFlags |= V63701_ASSERT_LINE_RESET;
+
+       uint8_t * sbp[1] = { soundBuffer };
+//     soundBuffer[0] = new uint8_t[1024];
+//     ptrToBuffer = (SAMPLE **)sbp;
+
+       if (YMInit(1, 3579580, 48000, 16, 512, (SAMPLE **)sbp))
+       {
+               WriteLog("YM: Could not init YM2151 emulator!\n");
+               return -1;
+       }
+
+       if (!LoadMemory())
+               return -1;
+
+       if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0)
+       {
+               WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError());
+               return -1;
+       }
+
+       SDL_Window * sdlWindow = NULL;
+       SDL_Renderer * sdlRenderer = NULL;
+       int retVal = SDL_CreateWindowAndRenderer(500, 400, SDL_WINDOW_OPENGL, &sdlWindow, &sdlRenderer);
+
+       if (retVal != 0)
+       {
+               WriteLog("Video: Could not window and/or renderer: %s\n", SDL_GetError());
+               return false;
+       }
+
+       InitSound();
+
+       bool done = false;
+       SDL_Event event;
+       startTicks = SDL_GetTicks();
+
+//try to get some noise outta this sucka
+//YMWriteReg(0, 0x20, 0xC0);  // Enable R+L channel
+//YMWriteReg(0, 0x21, 0xC0);  // Enable R+L channel
+YMWriteReg(0, 0x28, 0x4A);     // KC = A 440
+YMWriteReg(0, 0x29, 0x50);     // KC = C# 440+
+YMWriteReg(0, 0x2A, 0x34);     // KC = E 440-
+YMWriteReg(0, 0x40, 0x01);  // Write phase multiply of 1
+YMWriteReg(0, 0x41, 0x01);  // Write phase multiply of 1
+YMWriteReg(0, 0x42, 0x01);  // Write phase multiply of 1
+YMWriteReg(0, 0x60, 0x7F);  // Total level = 127
+YMWriteReg(0, 0x61, 0x7F);  // Total level = 127
+YMWriteReg(0, 0x62, 0x7F);  // Total level = 127
+YMWriteReg(0, 0xE0, 0x7F);  // 1st decay = 7, release = 15
+YMWriteReg(0, 0xE1, 0x7F);  // 1st decay = 7, release = 15
+YMWriteReg(0, 0xE2, 0x7F);  // 1st decay = 7, release = 15
+YMWriteReg(0, 0x08, 0x78);     // Key on channel #1, M1+C1+M2+C2
+YMWriteReg(0, 0x08, 0x79);     // Key on channel #2, M1+C1+M2+C2
+YMWriteReg(0, 0x08, 0x7A);     // Key on channel #3, M1+C1+M2+C2
+
+       do
+       {
+               while (SDL_PollEvent(&event))
+               {
+                       switch (event.type)
+                       {
+                       case SDL_KEYDOWN:
+                               if (event.key.keysym.sym == SDLK_ESCAPE)
+                                       done = true;
+                               else if (event.key.keysym.sym == SDLK_SPACE)
+                               {
+                                       YMWriteReg(0, 0x08, 0x40);
+                                       YMWriteReg(0, 0x08, 0x41);
+                                       YMWriteReg(0, 0x08, 0x42);
+                               }
+                               else if (event.key.keysym.sym == SDLK_1)
+                               {
+                                       memory[0x1380] = 1;
+                               }
+                               else if (event.key.keysym.sym == SDLK_2)
+                               {
+                                       memory[0x1380] = 2;
+                               }
+                               else if (event.key.keysym.sym == SDLK_3)
+                               {
+                                       memory[0x1380] = 3;
+                               }
+                               else if (event.key.keysym.sym == SDLK_4)
+                               {
+                                       memory[0x1380] = 4;
+                               }
+                               else if (event.key.keysym.sym == SDLK_5)
+                               {
+                                       memory[0x1380] = 5;
+                               }
+                               else if (event.key.keysym.sym == SDLK_6)
+                               {
+                                       memory[0x1380] = 6;
+                               }
+                               else if (event.key.keysym.sym == SDLK_7)
+                               {
+                                       memory[0x1380] = 7;
+                               }
+                               else if (event.key.keysym.sym == SDLK_8)
+                               {
+                                       memory[0x1380] = 8;
+                               }
+                               else if (event.key.keysym.sym == SDLK_9)
+                               {
+                                       memory[0x1380] = 9;
+                               }
+                               else if (event.key.keysym.sym == SDLK_0)
+                               {
+                                       memory[0x1380] = 10;
+                               }
+                               else if (event.key.keysym.sym == SDLK_q)
+                               {
+                                       memory[0x1380] = 11;
+                               }
+                               else if (event.key.keysym.sym == SDLK_w)
+                               {
+                                       memory[0x1380] = 12;
+                               }
+                               else if (event.key.keysym.sym == SDLK_e)
+                               {
+                                       memory[0x1380] = 13;
+                               }
+                       }
+               }
+
+               // 49152000/32 = 1536000 Hz, /60 = 25600
+               Execute63701(&mcu, 25600);
+
+               frameCount = (frameCount + 1) % 3;
+               uint32_t waitFrameTime = 17 - (frameCount == 0 ? 1 : 0);
+
+               // Wait for next frame...
+               while (SDL_GetTicks() - startTicks < waitFrameTime)
+                       SDL_Delay(1);
+
+               startTicks = SDL_GetTicks();
+       }
+       while (!done);
+
+       ShutdownSound();
+       SDL_Quit();
+//     delete[] soundBuffer[0];
+
+#if 0
+FILE * f = fopen("mcu-dis-$4000.txt", "w");
+uint32_t address = 0x4000;
+//for(uint32_t i=0xF000, i<0x10000; i++)
+do
+{
+       char line[8192];
+       address += Decode63701(memory, address, line);
+       fprintf(f, "%s\n", line);
+}
+while (address < 0xC000);
+
+fclose(f);
+#endif
+printf("MCU state:\nPC=%04X, S=%04X, X=%04X, D=%04X, CC=%02X\n", mcu.pc, mcu.s, mcu.x, mcu.d.word, mcu.cc);
+
+       return 0;
+}
+
+
+#if 0
+#define MCU_MEMORY(NAME,ADDR_LOWROM,ADDR_INPUT,ADDR_UNK1,ADDR_UNK2)                    \
+static MEMORY_READ_START( NAME##_mcu_readmem )                                                         \
+       { 0x0000, 0x001f, hd63701_internal_registers_r },                                               \
+       { 0x0080, 0x00ff, MRA_RAM },                                                                                    \
+       { 0x1000, 0x10ff, namcos1_wavedata_r }, /* PSG device, shared RAM */    \
+       { 0x1100, 0x113f, namcos1_sound_r }, /* PSG device, shared RAM */               \
+       { 0x1000, 0x13ff, shared1_r },                                                                                  \
+       { 0x1400, 0x1fff, MRA_RAM },                                                                                    \
+       { ADDR_INPUT+0x00, ADDR_INPUT+0x01, YM2151_status_port_0_r },                   \
+       { ADDR_INPUT+0x20, ADDR_INPUT+0x20, input_port_0_r },                                   \
+       { ADDR_INPUT+0x21, ADDR_INPUT+0x21, input_port_1_r },                                   \
+       { ADDR_INPUT+0x30, ADDR_INPUT+0x30, dsw0_r },                                                   \
+       { ADDR_INPUT+0x31, ADDR_INPUT+0x31, dsw1_r },                                                   \
+       { ADDR_LOWROM, ADDR_LOWROM+0x3fff, MRA_ROM },                                                   \
+       { 0x8000, 0xbfff, MRA_ROM },                                                                                    \
+       { 0xf000, 0xffff, MRA_ROM },                                                                                    \
+MEMORY_END                                                                                                                                     \
+                                                                                                                                                       \
+static MEMORY_WRITE_START( NAME##_mcu_writemem )                                                       \
+       { 0x0000, 0x001f, hd63701_internal_registers_w },                                               \
+       { 0x0080, 0x00ff, MWA_RAM },                                                                                    \
+       { 0x1000, 0x10ff, namcos1_wavedata_w }, /* PSG device, shared RAM */    \
+       { 0x1100, 0x113f, namcos1_sound_w }, /* PSG device, shared RAM */               \
+       { 0x1000, 0x13ff, shared1_w },                                                                                  \
+       { 0x1400, 0x1fff, MWA_RAM },                                                                                    \
+       { ADDR_INPUT+0x00, ADDR_INPUT+0x00, YM2151_register_port_0_w },                 \
+       { ADDR_INPUT+0x01, ADDR_INPUT+0x01, YM2151_data_port_0_w },                             \
+       { ADDR_UNK1, ADDR_UNK1, MWA_NOP }, /* ??? written (not always) at end of interrupt */   \
+       { ADDR_UNK2, ADDR_UNK2, MWA_NOP }, /* ??? written (not always) at end of interrupt */   \
+       { ADDR_LOWROM, ADDR_LOWROM+0x3fff, MWA_ROM },                                                   \
+       { 0x8000, 0xbfff, MWA_ROM },                                                                                    \
+       { 0xf000, 0xffff, MWA_ROM },                                                                                    \
+MEMORY_END
+
+#define UNUSED 0x4000
+/*                    LOWROM   INPUT    UNK1    UNK2 */
+MCU_MEMORY( rthunder, 0x4000, 0x2000, 0xb000, 0xb800 )
+#undef UNUSED
+
+/*
+0123456789ABCDEF
+----------------
+@ABCDEFGHIJKLMNO
+PQRSTUVWXYZ
+
+
+Sound: Successfully initialized.
+V63701 read $00 from $00...
+V63701 read $00 from $14...
+
+V63701 wrote $40 to $14...
+V63701 wrote $FF to $05...
+
+V63701 read $40 from $14...
+
+V63701 wrote $40 to $14...
+V63701 wrote $00 to $02...
+V63701 wrote $00 to $00...
+
+V63701 read $40 from $14...
+V63701 read $00 from $08...
+
+V63701 wrote $00 to $09...
+V63701 wrote $00 to $0A...
+V63701 wrote $01 to $0B...
+V63701 wrote $00 to $0C...
+
+V63701 read $00 from $08...
+
+V63701 wrote $08 to $08...
+
+V63701 read $00 from $02...
+V63701 read $00 from $02...
+V63701 read $00 from $02...
+
+V63701 wrote $00 to $03...
+V63701 wrote $18 to $01...
+V63701 wrote $C0 to $14...
+Sound: Done.
+MCU state:
+PC=814F, S=1FFD, X=8065, D=A600, CC=C4
+
+
+Problem:
+
+8110: 86 C0           LDAA  #$C0   
+ [PC=8112 S=1FFF X=1100 A=C0 B=00 CC=..N... TN=2C00 CT=2BBA OC=2C00 TO=3F00]
+8112: 97 14           STAA  $14    
+ [PC=8114 S=1FFF X=1100 A=C0 B=00 CC=..N... TN=2C00 CT=2BBC OC=2C00 TO=3F00]
+8114: DE AE           LDX   $AE    
+ [PC=8116 S=1FFF X=14F0 A=C0 B=00 CC=...... TN=2C00 CT=2BBF OC=2C00 TO=3F00]
+
+; Fetches 3 bytes here instead of 2!!
+8116: 6D 7A           TST   $7A,X  
+ [PC=8119 S=1FFF X=14F0 A=C0 B=00 CC=...... TN=2C00 CT=2BC3 OC=2C00 TO=3F00]
+
+; Execution is wrong at this point!!
+8119: 06              TAP          
+ [PC=811A S=1FFF X=14F0 A=C0 B=00 CC=...... TN=2C00 CT=2BC7 OC=2C00 TO=3F00]
+811A: 96 82           LDAA  $82    
+ [PC=811C S=1FFF X=14F0 A=00 B=00 CC=...Z.. TN=2C00 CT=2BC8 OC=2C00 TO=3F00]
+811C: 9B 80           ADDA  $80    
+ [PC=811E S=1FFF X=14F0 A=00 B=00 CC=...Z.. TN=2C00 CT=2BCB OC=2C00 TO=3F00]
+811E: 97 80           STAA  $80    
+ [PC=8120 S=1FFF X=14F0 A=00 B=00 CC=...Z.. TN=2C00 CT=2BCE OC=2C00 TO=3F00]
+8120: 7F 00 82        CLR   $0082  
+ [PC=8123 S=1FFF X=14F0 A=00 B=00 CC=...Z.. TN=2C00 CT=2BD1 OC=2C00 TO=3F00]
+8123: DE AA           LDX   $AA    
+ [PC=8125 S=1FFF X=8068 A=00 B=00 CC=..N... TN=2C00 CT=2BD6 OC=2C00 TO=3F00]
+8125: AD 00           JSR   $00,X  
+ [PC=8068 S=1FFD X=8068 A=00 B=00 CC=..N... TN=2C00 CT=2BDA OC=2C00 TO=3F00]
+8068: 39              RTS          
+ [PC=8127 S=1FFF X=8068 A=00 B=00 CC=..N... TN=2C00 CT=2BDF OC=2C00 TO=3F00]
+8127: 86 A6           LDAA  #$A6   
+ [PC=8129 S=1FFF X=8068 A=A6 B=00 CC=..N... TN=2C00 CT=2BE4 OC=2C00 TO=3F00]
+8129: B7 11 82        STAA  $1182  
+ [PC=812C S=1FFF X=8068 A=A6 B=00 CC=..N... TN=2C00 CT=2BE6 OC=2C00 TO=3F00]
+812C: DE AE           LDX   $AE    
+ [PC=812E S=1FFF X=14F0 A=A6 B=00 CC=...... TN=2C00 CT=2BEA OC=2C00 TO=3F00]
+812E: EE 2C           LDX   $2C,X  
+ [PC=8130 S=1FFF X=8065 A=A6 B=00 CC=..N... TN=2C00 CT=2BEE OC=2C00 TO=3F00]
+8130: AD 00           JSR   $00,X  
+ [PC=8065 S=1FFD X=8065 A=A6 B=00 CC=..N... TN=2C00 CT=2BF3 OC=2C00 TO=3F00]
+8065: B7 B0 00        STAA  $B000  
+ [PC=8068 S=1FFD X=8065 A=A6 B=00 CC=..N... TN=2C00 CT=2BF8 OC=2C00 TO=3F00]
+8068: 39              RTS          
+ [PC=8132 S=1FFF X=8065 A=A6 B=00 CC=..N... TN=2C00 CT=2BFC OC=2C00 TO=3F00]
+
+*/
+#endif
+
diff --git a/test/mcu-sound.mak b/test/mcu-sound.mak
new file mode 100644 (file)
index 0000000..2c554d6
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for MCU sound test
+
+OBJS = mcu-sound.o v63701.o ym2151.o dis63701.o
+LIBS = `sdl2-config --libs`
+
+all: mcu-sound
+
+.PHONY: clean
+
+clean:
+       -rm $(OBJS) mcu-sound
+
+%.o: %.cpp
+       g++ -c $< -g
+
+%.o: %.c
+       g++ -c $< -g
+
+mcu-sound: $(OBJS)
+       g++ -o $@ $(OBJS) $(LIBS)
+
+# Dependencies
+
+mcu-sound: mcu-sound.cpp
+mcu-sound.cpp: v63701.cpp v63701.h dis63701.cpp dis63701.h
+v63701.cpp: v63701.h
+dis63701.cpp: dis63701.h
+
+v63701.o: v63701.cpp v63701.h
+dis63701.o: dis63701.cpp dis63701.h
+mcu-sound.o: mcu-sound.cpp
+
index 99c32a3cdbaaf26dae2178af4fd7ecb2860c6b99..ef052abe4e1cc60122d17b4c5e4b4af53495ec3d 100644 (file)
@@ -1,49 +1,82 @@
 // Union bit fields...
 
-#include <dos.h>
-#include <conio.h>
-#include <fstream.h>
+//#include <dos.h>
+//#include <conio.h>
+#include <fstream>
+#include <iostream>
 #include <string.h>
-#include <iomanip.h>
+#include <iomanip>
 #include <stdio.h>
 #include <stdlib.h>
 
+using namespace std;
+
 union {
-  struct {
-    unsigned char C: 1;   // Carry flag 
-    unsigned char V: 1;   // oVerflow flag
-    unsigned char Z: 1;   // Zero flag
-    unsigned char N: 1;   // Negative flag
-    unsigned char I: 1;   // IRQ flag
-    unsigned char H: 1;   // Half carry flag
-    unsigned char F: 1;   // Fast IRQ flag
-    unsigned char E: 1;   // Entire flag
-    } flag;
-  unsigned char byte; } cc;
+       struct {
+               unsigned char C: 1;   // Carry flag 
+               unsigned char V: 1;   // oVerflow flag
+               unsigned char Z: 1;   // Zero flag
+               unsigned char N: 1;   // Negative flag
+               unsigned char I: 1;   // IRQ flag
+               unsigned char H: 1;   // Half carry flag
+               unsigned char F: 1;   // Fast IRQ flag
+               unsigned char E: 1;   // Entire flag
+               } flag;
+       unsigned char byte; } cc;
 
 union BYTE {
-  struct {
-    unsigned char b0: 1;  unsigned char b1: 1;  unsigned char b2: 1;
-    unsigned char b3: 1;  unsigned char b4: 1;  unsigned char b5: 1;
-    unsigned char b6: 1;  unsigned char b7: 1;
-    } bit;
-  unsigned char byte; };
+       struct {
+               unsigned char b0: 1;  unsigned char b1: 1;  unsigned char b2: 1;
+               unsigned char b3: 1;  unsigned char b4: 1;  unsigned char b5: 1;
+               unsigned char b6: 1;  unsigned char b7: 1;
+               } bit;
+       unsigned char byte; };
 
 union WORD {
-  struct { unsigned char lo: 8;  unsigned char hi: 8; } b;
-  unsigned int word; } hilo;
+       struct { unsigned char lo: 8;  unsigned char hi: 8; } b;
+       unsigned int word; } hilo;
+
 
-void main()
+int main(int, char * [])
 {
-  for(int i=0; i<256; i++)
-  {
-    cc.byte = i;
-    cout << cc.flag.E << " " << cc.flag.F << " "
-         << cc.flag.H << " " << cc.flag.I << " "
-         << cc.flag.N << " " << cc.flag.Z << " "
-         << cc.flag.V << " " << cc.flag.C << endl;
-  }
-  hilo.word = 0x6A44;
-  cout << hex << hilo.word << " "
-       << (int) hilo.b.lo << " " << (int) hilo.b.hi << endl;
+       for(int i=0; i<256; i++)
+       {
+               cc.byte = i;
+               cout << (cc.flag.E ? "1" : ".") << " " << (cc.flag.F ? "1" : ".") << " "
+                       << (cc.flag.H ? "1" : ".") << " " << (cc.flag.I ? "1" : ".") << " "
+                       << (cc.flag.N ? "1" : ".") << " " << (cc.flag.Z ? "1" : ".") << " "
+                       << (cc.flag.V ? "1" : ".") << " " << (cc.flag.C ? "1" : ".") << endl;
+       }
+
+       hilo.word = 0x6A44;
+       cout << hex << hilo.word << " "
+               << (int) hilo.b.lo << " " << (int) hilo.b.hi << endl;
+
+       BYTE b;
+       b.byte = (unsigned char)0xA5;
+       cout << "Byte = " << hex << (int)b.byte << ", bits = "
+               << (b.bit.b7 ? "1" : "0")
+               << (b.bit.b6 ? "1" : "0")
+               << (b.bit.b5 ? "1" : "0")
+               << (b.bit.b4 ? "1" : "0")
+               << (b.bit.b3 ? "1" : "0")
+               << (b.bit.b2 ? "1" : "0")
+               << (b.bit.b1 ? "1" : "0")
+               << (b.bit.b0 ? "1" : "0")
+               << endl;
+
+       b.bit.b4 ^= 1;
+       cout << "Byte = " << hex << (int)b.byte << ", bits = "
+               << (b.bit.b7 ? "1" : "0")
+               << (b.bit.b6 ? "1" : "0")
+               << (b.bit.b5 ? "1" : "0")
+               << (b.bit.b4 ? "1" : "0")
+               << (b.bit.b3 ? "1" : "0")
+               << (b.bit.b2 ? "1" : "0")
+               << (b.bit.b1 ? "1" : "0")
+               << (b.bit.b0 ? "1" : "0")
+               << endl;
+
+       return 0;
 }
+