C140: update emulator

with the one from C219 branch
This commit is contained in:
tildearrow 2023-08-27 15:52:54 -05:00
parent 440cbff5e4
commit 17a88fda70
6 changed files with 245 additions and 14 deletions

View file

@ -507,7 +507,7 @@ src/engine/platform/sound/d65modified.c
src/engine/platform/sound/ted-sound.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/oplAInterface.cpp
src/engine/platform/ym2608Interface.cpp src/engine/platform/ym2608Interface.cpp

View file

@ -21,7 +21,7 @@
#define _C140_H #define _C140_H
#include "../dispatch.h" #include "../dispatch.h"
#include "sound/c140.h" #include "sound/c140_c219.h"
#include "../fixedQueue.h" #include "../fixedQueue.h"
class DivPlatformC140: public DivDispatch { class DivPlatformC140: public DivDispatch {

View file

@ -2,7 +2,7 @@
============================================================================ ============================================================================
MODIFIED Namco C140 sound emulator - MODIFIED VERSION MODIFIED Namco C140/C219 sound emulator - MODIFIED VERSION
by cam900 by cam900
MODIFICATION by tildearrow - adds muting function and fixes overflow 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_max(int a, int b) { return (a > b) ? a : b; }
static int c140_min(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) void c140_voice_tick(struct c140_t *c140, const unsigned char v, const int cycle)
{ {
struct c140_voice_t *voice = &c140->voice[v]; 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) void c140_keyon(struct c140_voice_t *c140_voice)
{ {
c140_voice->busy = true; c140_voice->busy = true;
@ -125,6 +215,14 @@ void c140_keyon(struct c140_voice_t *c140_voice)
c140_voice->addr = c140_voice->start_addr; 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) void c140_init(struct c140_t *c140)
{ {
// u-law table verified from Wii Virtual Console Arcade Starblade // 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) void c140_reset(struct c140_t *c140)
{ {
for (int i = 0; i < 24; i++) 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) void c140_write(struct c140_t *c140, const unsigned short addr, const unsigned char data)
{ {
// voice register // voice register
@ -203,3 +365,47 @@ void c140_write(struct c140_t *c140, const unsigned short addr, const unsigned c
} }
// Timer // 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);
}
}
}

View file

@ -2,7 +2,7 @@
============================================================================ ============================================================================
MODIFIED Namco C140 sound emulator - MODIFIED VERSION MODIFIED Namco C140/C219 sound emulator - MODIFIED VERSION
by cam900 by cam900
MODIFICATION by tildearrow - adds muting function MODIFICATION by tildearrow - adds muting function
@ -41,8 +41,8 @@ TODO:
*/ */
#ifndef _C140_EMU_H #ifndef _C140_C219_EMU_H
#define _C140_EMU_H #define _C140_C219_EMU_H
#include <stdbool.h> #include <stdbool.h>
@ -58,13 +58,16 @@ struct c140_voice_t
bool keyon; // key on flag bool keyon; // key on flag
unsigned short freq; // sample frequency unsigned short freq; // sample frequency
unsigned char bank; // sample bank unsigned char bank; // sample bank
unsigned short start_addr; // sample start address unsigned int start_addr; // sample start address
unsigned short loop_addr; // sample loop address unsigned int loop_addr; // sample loop address
unsigned short end_addr; // sample end address unsigned int end_addr; // sample end address
int lvol, rvol; // left/right volume 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 compressed; // compressed sample flag
bool loop; // loop flag bool loop; // loop flag
unsigned short addr; // sample address unsigned int addr; // sample address
int frac; // frequency counter (.16 fixed point) int frac; // frequency counter (.16 fixed point)
int lout, rout; // left/right output int lout, rout; // left/right output
}; };
@ -77,20 +80,42 @@ struct c140_t
signed short *sample_mem; 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 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 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 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 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 c140_init(struct c140_t *c140);
void c219_init(struct c219_t *c219);
void c140_reset(struct c140_t *c140); void c140_reset(struct c140_t *c140);
void c219_reset(struct c219_t *c219);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif
#endif // _C140_EMU_H #endif // _C140_C219_EMU_H

View file

@ -199,7 +199,7 @@ const char* aboutLine[]={
"vgsound_emu (second version, modified version) by cam900", "vgsound_emu (second version, modified version) by cam900",
"SM8521 emulator (modified version) by cam900", "SM8521 emulator (modified version) by cam900",
"D65010G031 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:", "greetings to:",
"NEOART Costa Rica", "NEOART Costa Rica",

View file

@ -210,7 +210,7 @@ TAParamResult pVersion(String) {
printf("- ASAP POKEY emulator by Piotr Fusik ported to C++ by laoo (GPLv2)\n"); 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("- SM8521 emulator (modified version) by cam900 (zlib license)\n");
printf("- D65010G031 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; return TA_PARAM_QUIT;
} }