From 17a88fda703ef67c336426f109360ce9d9c32fe3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 27 Aug 2023 15:52:54 -0500 Subject: [PATCH] C140: update emulator with the one from C219 branch --- CMakeLists.txt | 2 +- src/engine/platform/c140.h | 2 +- .../platform/sound/{c140.c => c140_c219.c} | 210 +++++++++++++++++- .../platform/sound/{c140.h => c140_c219.h} | 41 +++- src/gui/about.cpp | 2 +- src/main.cpp | 2 +- 6 files changed, 245 insertions(+), 14 deletions(-) rename src/engine/platform/sound/{c140.c => c140_c219.c} (53%) rename src/engine/platform/sound/{c140.h => c140_c219.h} (71%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 47d7b3f9..63053c78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -507,7 +507,7 @@ src/engine/platform/sound/d65modified.c src/engine/platform/sound/ted-sound.c -src/engine/platform/sound/c140.c +src/engine/platform/sound/c140_c219.c src/engine/platform/oplAInterface.cpp src/engine/platform/ym2608Interface.cpp diff --git a/src/engine/platform/c140.h b/src/engine/platform/c140.h index 1c8ed907..6239c8f5 100644 --- a/src/engine/platform/c140.h +++ b/src/engine/platform/c140.h @@ -21,7 +21,7 @@ #define _C140_H #include "../dispatch.h" -#include "sound/c140.h" +#include "sound/c140_c219.h" #include "../fixedQueue.h" class DivPlatformC140: public DivDispatch { diff --git a/src/engine/platform/sound/c140.c b/src/engine/platform/sound/c140_c219.c similarity index 53% rename from src/engine/platform/sound/c140.c rename to src/engine/platform/sound/c140_c219.c index 9cdc4ecb..4855bd78 100644 --- a/src/engine/platform/sound/c140.c +++ b/src/engine/platform/sound/c140_c219.c @@ -2,7 +2,7 @@ ============================================================================ -MODIFIED Namco C140 sound emulator - MODIFIED VERSION +MODIFIED Namco C140/C219 sound emulator - MODIFIED VERSION by cam900 MODIFICATION by tildearrow - adds muting function and fixes overflow @@ -41,7 +41,7 @@ TODO: */ -#include "c140.h" +#include "c140_c219.h" static int c140_max(int a, int b) { return (a > b) ? a : b; } static int c140_min(int a, int b) { return (a < b) ? a : b; } @@ -61,6 +61,18 @@ void c140_tick(struct c140_t *c140, const int cycle) } } +void c219_tick(struct c219_t *c219, const int cycle) +{ + c219->lout = 0; + c219->rout = 0; + for (int i = 0; i < 16; i++) + { + c219_voice_tick(c219, i, cycle); + c219->lout += c219->voice[i].lout; + c219->rout += c219->voice[i].rout; + } +} + void c140_voice_tick(struct c140_t *c140, const unsigned char v, const int cycle) { struct c140_voice_t *voice = &c140->voice[v]; @@ -117,6 +129,84 @@ void c140_voice_tick(struct c140_t *c140, const unsigned char v, const int cycle } } +void c219_voice_tick(struct c219_t *c219, const unsigned char v, const int cycle) +{ + struct c140_voice_t *voice = &c219->voice[v]; + if (voice->busy && voice->keyon) + { + for (int c = 0; c < cycle; c++) + { + voice->frac += voice->freq; + if (voice->frac > 0xffff) + { + voice->addr += voice->frac >> 16; + if ((voice->addr >> 1) > voice->end_addr) + { + if (voice->loop) + { + voice->addr = (voice->addr + (voice->loop_addr << 1)) - (voice->end_addr << 1); + } + else + { + voice->keyon = false; + voice->lout = 0; + voice->rout = 0; + return; + } + } + if (voice->noise) + { + c219->lfsr = (c219->lfsr >> 1) ^ ((-(c219->lfsr & 1)) & 0xfff6); + } + voice->frac &= 0xffff; + } + } + if (!voice->muted) + { + signed int sample = 0; + if (voice->noise) + { + sample = (signed int)((signed short)(c219->lfsr)); + } + else + { + // fetch 8 bit sample + signed short s1 = c219->sample_mem[((unsigned int)(c219->bank[(v >> 2) & 3]) << 17) | voice->addr]; + signed short s2 = c219->sample_mem[((unsigned int)(c219->bank[(v >> 2) & 3]) << 17) | ((voice->addr + 1) & 0x1ffff)]; + if (voice->compressed) + { + s1 = c219->mulaw[s1]; + s2 = c219->mulaw[s2]; + } + else + { + s1 = (signed short)((signed char)(s1) << 8); + s2 = (signed short)((signed char)(s2) << 8); + } + if (voice->inv_sign) + { + s1 = -s1; + s2 = -s2; + } + // interpolate (originally was >>16, but I had to reduce it to 15 to prevent overflow) + sample = s1 + (((voice->frac >> 1) * (s2 - s1)) >> 15); + } + voice->lout = (voice->inv_lout ? (-sample) : sample) * voice->lvol; + voice->rout = sample * voice->rvol; + } + else + { + voice->lout = 0; + voice->rout = 0; + } + } + else + { + voice->lout = 0; + voice->rout = 0; + } +} + void c140_keyon(struct c140_voice_t *c140_voice) { c140_voice->busy = true; @@ -125,6 +215,14 @@ void c140_keyon(struct c140_voice_t *c140_voice) c140_voice->addr = c140_voice->start_addr; } +void c219_keyon(struct c140_voice_t *c140_voice) +{ + c140_voice->busy = true; + c140_voice->keyon = true; + c140_voice->frac = 0; + c140_voice->addr = c140_voice->start_addr << 1; +} + void c140_init(struct c140_t *c140) { // u-law table verified from Wii Virtual Console Arcade Starblade @@ -149,6 +247,41 @@ void c140_init(struct c140_t *c140) } } +void c219_init(struct c219_t *c219) +{ + // u-law table verified from Wii Virtual Console Arcade Knuckle Heads + for (int i = 0; i < 128; i++) + { + signed int compressed_sample = 0; + if (i < 16) + { + compressed_sample = 0x20 * i; + } + else if (i < 24) + { + compressed_sample = (0x200 + (0x40 * i)) - 0x400; + } + else if (i < 48) + { + compressed_sample = (0x400 + (0x80 * i)) - 0xc00; + } + else if (i < 100) + { + compressed_sample = (0x1000 + (0x100 * i)) - 0x3000; + } + else + { + compressed_sample = (0x4400 + (0x200 * i)) - 0xc800; + } + c219->mulaw[i] = compressed_sample; + c219->mulaw[i + 128] = (~compressed_sample) & 0xffe0; + } + for (int i = 0; i < 16; i++) + { + c219->voice[i].muted = false; + } +} + void c140_reset(struct c140_t *c140) { for (int i = 0; i < 24; i++) @@ -171,6 +304,35 @@ void c140_reset(struct c140_t *c140) } } +void c219_reset(struct c219_t *c219) +{ + for (int i = 0; i < 16; i++) + { + c219->voice[i].busy = false; + c219->voice[i].keyon = false; + c219->voice[i].freq = 0; + c219->voice[i].start_addr = 0; + c219->voice[i].loop_addr = 0; + c219->voice[i].end_addr = 0; + c219->voice[i].lvol = 0; + c219->voice[i].rvol = 0; + c219->voice[i].noise = false; + c219->voice[i].inv_lout = false; + c219->voice[i].inv_sign = false; + c219->voice[i].compressed = false; + c219->voice[i].loop = false; + c219->voice[i].addr = 0; + c219->voice[i].frac = 0; + c219->voice[i].lout = 0; + c219->voice[i].rout = 0; + } + c219->lfsr = 0x1234; + for (int i = 0; i < 4; i++) + { + c219->bank[i] = 0; + } +} + void c140_write(struct c140_t *c140, const unsigned short addr, const unsigned char data) { // voice register @@ -203,3 +365,47 @@ void c140_write(struct c140_t *c140, const unsigned short addr, const unsigned c } // Timer } + +void c219_write(struct c219_t *c219, const unsigned short addr, const unsigned char data) +{ + // voice register + if (addr < 0x180) + { + struct c140_voice_t *voice = &c219->voice[addr >> 4]; + switch (addr & 0xf) + { + case 0x0: voice->rvol = data; break; + case 0x1: voice->lvol = data; break; + case 0x2: voice->freq = (voice->freq & ~0xff00) | (unsigned int)(data << 8); break; + case 0x3: voice->freq = (voice->freq & ~0x00ff) | data; break; + //case 0x4: break; // Unknown + case 0x5: + voice->compressed = c140_bit(data, 0); + voice->noise = c140_bit(data, 2); + voice->inv_lout = c140_bit(data, 3); + voice->loop = c140_bit(data, 4); + voice->inv_sign = c140_bit(data, 6); + if (data & 0x80) + c219_keyon(voice); + else + voice->busy = false; + break; + case 0x6: voice->start_addr = (voice->start_addr & ~0xff00) | (unsigned int)(data << 8); break; + case 0x7: voice->start_addr = (voice->start_addr & ~0x00ff) | data; break; + case 0x8: voice->end_addr = (voice->end_addr & ~0xff00) | (unsigned int)(data << 8); break; + case 0x9: voice->end_addr = (voice->end_addr & ~0x00ff) | data; break; + case 0xa: voice->loop_addr = (voice->loop_addr & ~0xff00) | (unsigned int)(data << 8); break; + case 0xb: voice->loop_addr = (voice->loop_addr & ~0x00ff) | data; break; + default: break; + } + } + // bank + else if (addr >= 0x1f0) + { + if (addr & 1) + { + const unsigned short bankaddr = (addr >> 1) & 3; + c219->bank[(bankaddr + 1) & 3] = (data & 3); + } + } +} diff --git a/src/engine/platform/sound/c140.h b/src/engine/platform/sound/c140_c219.h similarity index 71% rename from src/engine/platform/sound/c140.h rename to src/engine/platform/sound/c140_c219.h index 21a8b8a4..fb19125a 100644 --- a/src/engine/platform/sound/c140.h +++ b/src/engine/platform/sound/c140_c219.h @@ -2,7 +2,7 @@ ============================================================================ -MODIFIED Namco C140 sound emulator - MODIFIED VERSION +MODIFIED Namco C140/C219 sound emulator - MODIFIED VERSION by cam900 MODIFICATION by tildearrow - adds muting function @@ -41,8 +41,8 @@ TODO: */ -#ifndef _C140_EMU_H -#define _C140_EMU_H +#ifndef _C140_C219_EMU_H +#define _C140_C219_EMU_H #include @@ -58,13 +58,16 @@ struct c140_voice_t bool keyon; // key on flag unsigned short freq; // sample frequency unsigned char bank; // sample bank - unsigned short start_addr; // sample start address - unsigned short loop_addr; // sample loop address - unsigned short end_addr; // sample end address + unsigned int start_addr; // sample start address + unsigned int loop_addr; // sample loop address + unsigned int end_addr; // sample end address int lvol, rvol; // left/right volume + bool noise; // noise flag + bool inv_lout; // invert left output flag + bool inv_sign; // invert sign bit flag bool compressed; // compressed sample flag bool loop; // loop flag - unsigned short addr; // sample address + unsigned int addr; // sample address int frac; // frequency counter (.16 fixed point) int lout, rout; // left/right output }; @@ -77,20 +80,42 @@ struct c140_t signed short *sample_mem; }; +struct c219_t +{ + struct c140_voice_t voice[16]; + signed int lout, rout; + signed short mulaw[256]; + unsigned short lfsr; + unsigned char bank[4]; + signed char *sample_mem; +}; + void c140_tick(struct c140_t *c140, const int cycle); +void c219_tick(struct c219_t *c219, const int cycle); + void c140_voice_tick(struct c140_t *c140, const unsigned char v, const int cycle); +void c219_voice_tick(struct c219_t *c219, const unsigned char v, const int cycle); + void c140_keyon(struct c140_voice_t *c140_voice); +void c219_keyon(struct c140_voice_t *c140_voice); + void c140_write(struct c140_t *c140, const unsigned short addr, const unsigned char data); +void c219_write(struct c219_t *c219, const unsigned short addr, const unsigned char data); + void c140_init(struct c140_t *c140); +void c219_init(struct c219_t *c219); + void c140_reset(struct c140_t *c140); +void c219_reset(struct c219_t *c219); + #ifdef __cplusplus } // extern "C" #endif -#endif // _C140_EMU_H +#endif // _C140_C219_EMU_H diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 2151f5c6..e1ce1b64 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -199,7 +199,7 @@ const char* aboutLine[]={ "vgsound_emu (second version, modified version) by cam900", "SM8521 emulator (modified version) by cam900", "D65010G031 emulator (modified version) by cam900", - "Namco C140 (modified version) emulator by cam900", + "Namco C140/C219 emulator (modified version) by cam900", "", "greetings to:", "NEOART Costa Rica", diff --git a/src/main.cpp b/src/main.cpp index 2c12678a..dd0cc2bf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -210,7 +210,7 @@ TAParamResult pVersion(String) { printf("- ASAP POKEY emulator by Piotr Fusik ported to C++ by laoo (GPLv2)\n"); printf("- SM8521 emulator (modified version) by cam900 (zlib license)\n"); printf("- D65010G031 emulator (modified version) by cam900 (zlib license)\n"); - printf("- C140 emulator (modified version) by cam900 (zlib license)\n"); + printf("- C140/C219 emulator (modified version) by cam900 (zlib license)\n"); return TA_PARAM_QUIT; }