From 756fbabdd25117272164fd2d31810e6018a5990e Mon Sep 17 00:00:00 2001 From: Shamus Hammons Date: Thu, 17 Apr 2014 08:39:09 -0500 Subject: [PATCH] Added V63701, YM2151 emulation, more SDL 2 fixes. --- makefile | 4 +- src/screen.cpp | 254 +--- src/thunder.cpp | 12 + src/v63701.cpp | 2920 ++++++++++++++++++++++++++++++++++++++++++++ src/v63701.h | 132 ++ src/ym2151.c | 1437 ++++++++++++++++++++++ src/ym2151.h | 160 +++ test/mcu-sound.cpp | 557 +++++++++ test/mcu-sound.mak | 32 + test/union.cpp | 103 +- 10 files changed, 5327 insertions(+), 284 deletions(-) create mode 100644 src/v63701.cpp create mode 100644 src/v63701.h create mode 100644 src/ym2151.c create mode 100644 src/ym2151.h create mode 100644 test/mcu-sound.cpp create mode 100644 test/mcu-sound.mak diff --git a/makefile b/makefile index 966bf3b..9004c58 100644 --- 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 \ diff --git a/src/screen.cpp b/src/screen.cpp index bc73360..f72b2bf 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -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> 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 } } } diff --git a/src/thunder.cpp b/src/thunder.cpp index 5982424..9439026 100644 --- a/src/thunder.cpp +++ b/src/thunder.cpp @@ -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 index 0000000..3f01921 --- /dev/null +++ b/src/v63701.cpp @@ -0,0 +1,2920 @@ +// +// Virtual 63701 Emulator v1.0 +// +// by James Hammons +// (C) 2014 Underground Software +// +// JLH = James Hammons +// +// 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 // 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= 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, ®s, 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 index 0000000..47c5b18 --- /dev/null +++ b/src/v63701.h @@ -0,0 +1,132 @@ +// +// Virtual 63701 Header file +// +// by James Hammons +// (C) 2014 Underground Software +// + +#ifndef __V63701_H__ +#define __V63701_H__ + +#include + +// 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 index 0000000..46776ee --- /dev/null +++ b/src/ym2151.c @@ -0,0 +1,1437 @@ +#include +#include +#include +#include +#include +//#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< 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 -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> 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)<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; iBuf, '\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> 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 +#include +#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 index 0000000..2c554d6 --- /dev/null +++ b/test/mcu-sound.mak @@ -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 + diff --git a/test/union.cpp b/test/union.cpp index 99c32a3..ef052ab 100644 --- a/test/union.cpp +++ b/test/union.cpp @@ -1,49 +1,82 @@ // Union bit fields... -#include -#include -#include +//#include +//#include +#include +#include #include -#include +#include #include #include +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; } + -- 2.37.2