taito_zm: DSP emulation (work in progress!) (#3854)

* taito_zm.cpp : Updates
Add DSP, Reduce MCFGs, Add device_mixer_interface for sound gain, Add imperfect_features related to DSP, Add notes

* taito_zm.cpp : Fix TMS57002 clock

* Improve Taito Zoom ZSG-2 sound emulation

zsg2.cpp: implement emphasis filter, this is a noise reduction scheme
that amplifies higher frequncies to reduce quantization noise.

zsg2.cpp: Add sample interpolation and another adjustable lowpass
filter. This seems to be roughly what real hardware does...

zsg2.cpp: Improve panning registers and identify DSP output gain
registers.

* zsg2: minor changes [nw]

zsg2: Register 0b appears to be status flags [nw]

zsg2: Linear ramping probably makes more sense [nw]

* zsg2: slight adjustment of emphasis filter [nw]

* zsg2: slight adjustment of emphasis filter #2 [nw]

* zsg2: more sober ramping algorithm [nw]

* tms57002: add instructions 3c/3d, make them behave as NOP as they're undocumented and not understood

* tms57002: Add dready callback for superctr (nw)

* tms57002: Fixes to make Taito Zoom DSP working

tms57002: Add undocumented instruction saom / raom, they set saturation
mode for the ALU.

tms57002: Implement MACC pipeline.

tms57002: Add callbacks for EMPTY and PC0 pins.

tms57002: Add a few unimplemented instructions.

tms57002: Proper behavior of CMEM UPLOAD mode.

tms57002: Fix an issue where program is not properly loaded if PLOAD is
set after a program has already been written.

* Documentation fix, properly identified registers as ramping control, will implement that soon [nw]

* taito_zm: Working DSP emulation

Pretty much OST quality now. A pretty decent upgrade from how it was
previously, I'd say.

* typo [nw]

* just adding some quick notes about the WIP [nw]

* Fix build [nw]

* zsg2: Proper ramping implemenation, add register map, minor cleanups

* oops [nw]
This commit is contained in:
superctr 2018-08-17 15:58:29 +02:00 committed by R. Belmont
parent 59c2a0536c
commit 3b57b7e90c
11 changed files with 334 additions and 153 deletions

View File

@ -25,8 +25,10 @@ void tms57002_device::internal_pgm(address_map &map)
tms57002_device::tms57002_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) tms57002_device::tms57002_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: cpu_device(mconfig, TMS57002, tag, owner, clock) : cpu_device(mconfig, TMS57002, tag, owner, clock)
, device_sound_interface(mconfig, *this) , device_sound_interface(mconfig, *this)
, macc(0), st0(0), st1(0), sti(0), txrd(0) , macc(0), macc_read(0), macc_write(0), st0(0), st1(0), sti(0), txrd(0)
, m_dready_callback(*this) , m_dready_callback(*this)
, m_pc0_callback(*this)
, m_empty_callback(*this)
, program_config("program", ENDIANNESS_LITTLE, 32, 8, -2, address_map_constructor(FUNC(tms57002_device::internal_pgm), this)) , program_config("program", ENDIANNESS_LITTLE, 32, 8, -2, address_map_constructor(FUNC(tms57002_device::internal_pgm), this))
, data_config("data", ENDIANNESS_LITTLE, 8, 20) , data_config("data", ENDIANNESS_LITTLE, 8, 20)
{ {
@ -47,7 +49,9 @@ WRITE_LINE_MEMBER(tms57002_device::pload_w)
if(olds ^ sti) { if(olds ^ sti) {
if (sti & IN_PLOAD) { if (sti & IN_PLOAD) {
hidx = 0; hidx = 0;
hpc = 0; pc = 0;
ca = 0;
sti &= ~(SU_MASK);
} }
} }
} }
@ -62,26 +66,30 @@ WRITE_LINE_MEMBER(tms57002_device::cload_w)
if(olds ^ sti) { if(olds ^ sti) {
if (sti & IN_CLOAD) { if (sti & IN_CLOAD) {
hidx = 0; hidx = 0;
ca = 0; //ca = 0; // Seems extremely dubious
} }
} }
} }
void tms57002_device::device_reset() void tms57002_device::device_reset()
{ {
sti = (sti & ~(SU_MASK|S_READ|S_WRITE|S_BRANCH|S_HOST)) | (SU_ST0|S_IDLE); sti = (sti & ~(SU_MASK|S_READ|S_WRITE|S_BRANCH|S_HOST|S_UPDATE)) | (SU_ST0|S_IDLE);
pc = 0; pc = 0;
ca = 0; ca = 0;
hidx = 0; hidx = 0;
id = 0; id = 0;
ba0 = 0; ba0 = 0;
ba1 = 0; ba1 = 0;
update_counter_tail = 0;
update_counter_head = 0;
st0 &= ~(ST0_INCS | ST0_DIRI | ST0_FI | ST0_SIM | ST0_PLRI | st0 &= ~(ST0_INCS | ST0_DIRI | ST0_FI | ST0_SIM | ST0_PLRI |
ST0_PBCI | ST0_DIRO | ST0_FO | ST0_SOM | ST0_PLRO | ST0_PBCI | ST0_DIRO | ST0_FO | ST0_SOM | ST0_PLRO |
ST0_PBCO | ST0_CNS); ST0_PBCO | ST0_CNS);
st1 &= ~(ST1_AOV | ST1_SFAI | ST1_SFAO | ST1_MOVM | ST1_MOV | st1 &= ~(ST1_AOV | ST1_SFAI | ST1_SFAO | ST1_AOVM | ST1_MOVM | ST1_MOV |
ST1_SFMA | ST1_SFMO | ST1_RND | ST1_CRM | ST1_DBP); ST1_SFMA | ST1_SFMO | ST1_RND | ST1_CRM | ST1_DBP);
update_dready(); update_dready();
update_pc0();
update_empty();
xba = 0; xba = 0;
xoa = 0; xoa = 0;
@ -112,6 +120,7 @@ WRITE8_MEMBER(tms57002_device::data_w)
break; break;
case SU_PRG: case SU_PRG:
program->write_dword(pc++, val); program->write_dword(pc++, val);
update_pc0();
break; break;
} }
} }
@ -121,9 +130,11 @@ WRITE8_MEMBER(tms57002_device::data_w)
host[hidx++] = data; host[hidx++] = data;
if(hidx >= 4) { if(hidx >= 4) {
uint32_t val = (host[0]<<24) | (host[1]<<16) | (host[2]<<8) | host[3]; uint32_t val = (host[0]<<24) | (host[1]<<16) | (host[2]<<8) | host[3];
cmem[sa] = val;
sti &= ~SU_CVAL; sti &= ~SU_CVAL;
allow_update = 0; update[update_counter_head] = val;
update_counter_head = (update_counter_head + 1) & 0x0f;
hidx = 1; // the write shouldn't really happen until CLOAD is high though
update_empty();
} }
} else { } else {
sa = data; sa = data;
@ -160,11 +171,6 @@ READ8_MEMBER(tms57002_device::data_r)
return res; return res;
} }
READ_LINE_MEMBER(tms57002_device::empty_r)
{
return 1;
}
READ_LINE_MEMBER(tms57002_device::dready_r) READ_LINE_MEMBER(tms57002_device::dready_r)
{ {
return sti & S_HOST ? 0 : 1; return sti & S_HOST ? 0 : 1;
@ -180,9 +186,24 @@ READ_LINE_MEMBER(tms57002_device::pc0_r)
return pc == 0 ? 0 : 1; return pc == 0 ? 0 : 1;
} }
void tms57002_device::update_pc0()
{
m_pc0_callback(pc == 0 ? 0 : 1);
}
READ_LINE_MEMBER(tms57002_device::empty_r)
{
return (update_counter_head == update_counter_tail);
}
void tms57002_device::update_empty()
{
m_empty_callback(update_counter_head == update_counter_tail);
}
WRITE_LINE_MEMBER(tms57002_device::sync_w) WRITE_LINE_MEMBER(tms57002_device::sync_w)
{ {
if(sti & (IN_PLOAD | IN_CLOAD)) if(sti & (IN_PLOAD /*| IN_CLOAD*/))
return; return;
allow_update = 1; allow_update = 1;
@ -293,7 +314,7 @@ inline void tms57002_device::xm_step_write()
int64_t tms57002_device::macc_to_output_0(int64_t rounding, uint64_t rmask) int64_t tms57002_device::macc_to_output_0(int64_t rounding, uint64_t rmask)
{ {
int64_t m = macc; int64_t m = macc_read;
uint64_t m1; uint64_t m1;
int over = 0; int over = 0;
@ -318,7 +339,7 @@ int64_t tms57002_device::macc_to_output_0(int64_t rounding, uint64_t rmask)
int64_t tms57002_device::macc_to_output_1(int64_t rounding, uint64_t rmask) int64_t tms57002_device::macc_to_output_1(int64_t rounding, uint64_t rmask)
{ {
int64_t m = macc; int64_t m = macc_read;
uint64_t m1; uint64_t m1;
int over = 0; int over = 0;
@ -344,7 +365,7 @@ int64_t tms57002_device::macc_to_output_1(int64_t rounding, uint64_t rmask)
int64_t tms57002_device::macc_to_output_2(int64_t rounding, uint64_t rmask) int64_t tms57002_device::macc_to_output_2(int64_t rounding, uint64_t rmask)
{ {
int64_t m = macc; int64_t m = macc_read;
uint64_t m1; uint64_t m1;
int over = 0; int over = 0;
@ -370,7 +391,7 @@ int64_t tms57002_device::macc_to_output_2(int64_t rounding, uint64_t rmask)
int64_t tms57002_device::macc_to_output_3(int64_t rounding, uint64_t rmask) int64_t tms57002_device::macc_to_output_3(int64_t rounding, uint64_t rmask)
{ {
int64_t m = macc; int64_t m = macc_read;
uint64_t m1; uint64_t m1;
int over = 0; int over = 0;
@ -393,7 +414,7 @@ int64_t tms57002_device::macc_to_output_3(int64_t rounding, uint64_t rmask)
int64_t tms57002_device::macc_to_output_0s(int64_t rounding, uint64_t rmask) int64_t tms57002_device::macc_to_output_0s(int64_t rounding, uint64_t rmask)
{ {
int64_t m = macc; int64_t m = macc_read;
uint64_t m1; uint64_t m1;
int over = 0; int over = 0;
@ -422,7 +443,7 @@ int64_t tms57002_device::macc_to_output_0s(int64_t rounding, uint64_t rmask)
int64_t tms57002_device::macc_to_output_1s(int64_t rounding, uint64_t rmask) int64_t tms57002_device::macc_to_output_1s(int64_t rounding, uint64_t rmask)
{ {
int64_t m = macc; int64_t m = macc_read;
uint64_t m1; uint64_t m1;
int over = 0; int over = 0;
@ -452,7 +473,7 @@ int64_t tms57002_device::macc_to_output_1s(int64_t rounding, uint64_t rmask)
int64_t tms57002_device::macc_to_output_2s(int64_t rounding, uint64_t rmask) int64_t tms57002_device::macc_to_output_2s(int64_t rounding, uint64_t rmask)
{ {
int64_t m = macc; int64_t m = macc_read;
uint64_t m1; uint64_t m1;
int over = 0; int over = 0;
@ -482,7 +503,7 @@ int64_t tms57002_device::macc_to_output_2s(int64_t rounding, uint64_t rmask)
int64_t tms57002_device::macc_to_output_3s(int64_t rounding, uint64_t rmask) int64_t tms57002_device::macc_to_output_3s(int64_t rounding, uint64_t rmask)
{ {
int64_t m = macc; int64_t m = macc_read;
uint64_t m1; uint64_t m1;
int over = 0; int over = 0;
@ -607,6 +628,34 @@ int64_t tms57002_device::check_macc_overflow_3s()
return macc; return macc;
} }
uint32_t tms57002_device::get_cmem(uint8_t addr)
{
if(sa == addr && update_counter_head != update_counter_tail)
sti |= S_UPDATE;
if(sti & S_UPDATE)
{
cmem[addr] = update[update_counter_tail];
update_counter_tail = (update_counter_tail + 1) & 0x0f;
update_empty();
if(update_counter_head == update_counter_tail)
sti &= ~S_UPDATE;
return cmem[addr]; // The value of crm is ignored during an update.
}
else
{
int crm = (st1 & ST1_CRM) >> ST1_CRM_SHIFT;
uint32_t cvar = cmem[addr];
if(crm == 1)
return (cvar & 0xffff0000);
else if(crm == 2)
return (cvar << 16);
return cvar;
}
}
void tms57002_device::cache_flush() void tms57002_device::cache_flush()
{ {
int i; int i;
@ -730,7 +779,7 @@ void tms57002_device::execute_run()
{ {
int ipc = -1; int ipc = -1;
while(icount > 0 && !(sti & (S_IDLE | IN_PLOAD | IN_CLOAD))) { while(icount > 0 && !(sti & (S_IDLE | IN_PLOAD /*| IN_CLOAD*/))) {
int iipc; int iipc;
debugger_instruction_hook(pc); debugger_instruction_hook(pc);
@ -746,6 +795,9 @@ void tms57002_device::execute_run()
else else
xm_step_write(); xm_step_write();
} }
macc_read = macc_write;
macc_write = macc;
for(;;) { for(;;) {
uint32_t c, d; uint32_t c, d;
@ -786,8 +838,10 @@ void tms57002_device::execute_run()
} else if(sti & S_BRANCH) { } else if(sti & S_BRANCH) {
sti &= ~S_BRANCH; sti &= ~S_BRANCH;
ipc = -1; ipc = -1;
} else } else {
pc++; // Wraps if it reaches 256, next wraps too pc++; // Wraps if it reaches 256, next wraps too
update_pc0();
}
if(rptc_next) { if(rptc_next) {
rptc = rptc_next; rptc = rptc_next;
@ -825,6 +879,8 @@ void tms57002_device::sound_stream_update(sound_stream &stream, stream_sample_t
void tms57002_device::device_resolve_objects() void tms57002_device::device_resolve_objects()
{ {
m_dready_callback.resolve_safe(); m_dready_callback.resolve_safe();
m_pc0_callback.resolve_safe();
m_empty_callback.resolve_safe();
} }
void tms57002_device::device_start() void tms57002_device::device_start()
@ -861,10 +917,13 @@ void tms57002_device::device_start()
stream_alloc(4, 4, STREAM_SYNC); stream_alloc(4, 4, STREAM_SYNC);
save_item(NAME(macc)); save_item(NAME(macc));
save_item(NAME(macc_read));
save_item(NAME(macc_write));
save_item(NAME(cmem)); save_item(NAME(cmem));
save_item(NAME(dmem0)); save_item(NAME(dmem0));
save_item(NAME(dmem1)); save_item(NAME(dmem1));
save_item(NAME(update));
save_item(NAME(si)); save_item(NAME(si));
save_item(NAME(so)); save_item(NAME(so));
@ -893,6 +952,9 @@ void tms57002_device::device_start()
save_item(NAME(host)); save_item(NAME(host));
save_item(NAME(hidx)); save_item(NAME(hidx));
save_item(NAME(update_counter_head));
save_item(NAME(update_counter_tail));
save_item(NAME(allow_update)); save_item(NAME(allow_update));
} }

View File

@ -17,6 +17,8 @@ public:
tms57002_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); tms57002_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
auto dready_callback() { return m_dready_callback.bind(); } auto dready_callback() { return m_dready_callback.bind(); }
auto pc0_callback() { return m_pc0_callback.bind(); }
auto empty_callback() { return m_empty_callback.bind(); }
DECLARE_READ8_MEMBER(data_r); DECLARE_READ8_MEMBER(data_r);
DECLARE_WRITE8_MEMBER(data_w); DECLARE_WRITE8_MEMBER(data_w);
@ -51,7 +53,8 @@ private:
S_READ = 0x00000040, S_READ = 0x00000040,
S_WRITE = 0x00000080, S_WRITE = 0x00000080,
S_BRANCH = 0x00000100, S_BRANCH = 0x00000100,
S_HOST = 0x00000200 S_HOST = 0x00000200,
S_UPDATE = 0x00000400,
}; };
enum { enum {
@ -75,6 +78,7 @@ private:
ST1_AOV = 0x000001, ST1_AOV = 0x000001,
ST1_SFAI = 0x000002, ST1_SFAI = 0x000002,
ST1_SFAO = 0x000004, ST1_SFAO = 0x000004,
ST1_AOVM = 0x000008, // undocumented!
ST1_MOVM = 0x000020, ST1_MOVM = 0x000020,
ST1_MOV = 0x000040, ST1_MOV = 0x000040,
ST1_SFMA = 0x000180, ST1_SFMA_SHIFT = 7, ST1_SFMA = 0x000180, ST1_SFMA_SHIFT = 7,
@ -120,7 +124,8 @@ private:
short ipc; short ipc;
}; };
int64_t macc; // macc_read and macc_write are used by non-pipelined instructions
int64_t macc, macc_read, macc_write;
uint32_t cmem[256]; uint32_t cmem[256];
uint32_t dmem0[256]; uint32_t dmem0[256];
@ -136,10 +141,15 @@ private:
uint32_t xm_adr; uint32_t xm_adr;
uint8_t host[4], hidx, allow_update; uint8_t host[4], hidx, allow_update;
uint32_t update[16];
uint8_t update_counter_head, update_counter_tail;
cd cache; cd cache;
devcb_write_line m_dready_callback; devcb_write_line m_dready_callback;
devcb_write_line m_pc0_callback;
devcb_write_line m_empty_callback;
const address_space_config program_config, data_config; const address_space_config program_config, data_config;
@ -164,6 +174,8 @@ private:
inline int sfma(uint32_t st1); inline int sfma(uint32_t st1);
void update_dready(); void update_dready();
void update_pc0();
void update_empty();
void xm_init(); void xm_init();
void xm_step_read(); void xm_step_read();
@ -190,6 +202,7 @@ private:
short get_hash(unsigned char adr, uint32_t st1, short *pnode); short get_hash(unsigned char adr, uint32_t st1, short *pnode);
short get_hashnode(unsigned char adr, uint32_t st1, short pnode); short get_hashnode(unsigned char adr, uint32_t st1, short pnode);
int decode_get_pc(); int decode_get_pc();
uint32_t get_cmem(uint8_t addr);
}; };
enum { enum {

View File

@ -11,6 +11,7 @@
#include "emu.h" #include "emu.h"
#include "debugger.h" #include "debugger.h"
#include "tms57002.h" #include "tms57002.h"
#include <algorithm>
inline int tms57002_device::xmode(uint32_t opcode, char type, cstate *cs) inline int tms57002_device::xmode(uint32_t opcode, char type, cstate *cs)
{ {
@ -40,6 +41,10 @@ inline int tms57002_device::dbp(uint32_t st1)
inline int tms57002_device::crm(uint32_t st1) inline int tms57002_device::crm(uint32_t st1)
{ {
// value overridden during cvar update
if(update_counter_head != update_counter_tail)
return 0;
int crm = (st1 & ST1_CRM) >> ST1_CRM_SHIFT; int crm = (st1 & ST1_CRM) >> ST1_CRM_SHIFT;
return crm <= 2 ? crm : 0; return crm <= 2 ? crm : 0;
} }
@ -90,6 +95,7 @@ void tms57002_device::decode_cat1(uint32_t opcode, unsigned short *op, cstate *c
#undef CDEC1 #undef CDEC1
default: default:
logerror("Unhandled cat1 opcode %02x\n",opcode >> 18);
decode_error(opcode); decode_error(opcode);
break; break;
} }
@ -106,6 +112,7 @@ void tms57002_device::decode_cat2_pre(uint32_t opcode, unsigned short *op, cstat
#undef CDEC2A #undef CDEC2A
default: default:
logerror("Unhandled cat2_pre opcode %02x \n",(opcode >> 11) & 0x7f);
decode_error(opcode); decode_error(opcode);
break; break;
} }
@ -122,6 +129,7 @@ void tms57002_device::decode_cat2_post(uint32_t opcode, unsigned short *op, csta
#undef CDEC2B #undef CDEC2B
default: default:
logerror("Unhandled cat2_post opcode %02x\n",(opcode >> 11) & 0x7f);
decode_error(opcode); decode_error(opcode);
break; break;
} }
@ -138,6 +146,7 @@ void tms57002_device::decode_cat3(uint32_t opcode, unsigned short *op, cstate *c
#undef CDEC3 #undef CDEC3
default: default:
logerror("Unhandled cat3 opcode %02x\n",(opcode >> 11) & 0x7f);
decode_error(opcode); decode_error(opcode);
break; break;
} }

View File

@ -219,17 +219,17 @@ lirk 3 20 1 n
lmhc 1 33 1 n lmhc 1 33 1 n
lmhc %c lmhc %c
macc = ((int64_t)(int32_t)%c) << 16; macc_write = macc = ((int64_t)(int32_t)%c) << 16;
lmhd 1 31 1 n lmhd 1 31 1 n
lmhd %d lmhd %d
macc = ((int64_t)(int32_t)%d) << 16; macc_write = macc = ((int64_t)(int32_t)%d) << 16;
lmld 1 32 1 n lmld 1 32 1 n
lmld %d lmld %d
macc = (macc & ~0xffffffULL) | %d24; macc_write = macc = (macc & ~0xffffffULL) | %d24;
lpc 2b 31 1 n lpc 2a 31 1 n
lpc %c lpc %c
if(sti & S_HOST) if(sti & S_HOST)
break; break;
@ -242,7 +242,7 @@ lpc 2b 31 1 n
sti |= S_HOST; sti |= S_HOST;
update_dready(); update_dready();
lpd 2b 30 1 n lpd 2a 30 1 n
lpd %d lpd %d
mac 1 24 1 y mac 1 24 1 y
@ -256,6 +256,12 @@ mac 1 24 1 y
mac 1 25 1 y mac 1 25 1 y
mac a,%d mac a,%d
d = %d24;
if(d & 0x00800000)
d |= 0xff000000;
creg = c = %a;
r = (int64_t)(int32_t)c * (int64_t)(int32_t)d;
macc = %ml + (r >> 7);
mac 1 26 1 y mac 1 26 1 y
mac %c,a mac %c,a
@ -310,6 +316,7 @@ mpyu 1 28 1 y
neg 1 02 1 n neg 1 02 1 n
neg neg
%wa(-(int64_t)%a);
or 1 17 1 n or 1 17 1 n
or %d,a or %d,a
@ -408,11 +415,11 @@ scrm 2a 4b 1 n f
scrm <3> scrm <3>
st1 = (st1 & ~ST1_CRM) | (3 << ST1_CRM_SHIFT); st1 = (st1 & ~ST1_CRM) | (3 << ST1_CRM_SHIFT);
sfai 2b 54 1 n f sfai 2a 54 1 n f
sfai 0 sfai 0
st1 &= ~ST1_SFAI; st1 &= ~ST1_SFAI;
sfai 2b 55 1 n f sfai 2a 55 1 n f
sfai -1 sfai -1
st1 |= ST1_SFAI; st1 |= ST1_SFAI;
@ -422,21 +429,21 @@ sfao 2a 50 1 n f
sfao 2a 51 1 n f sfao 2a 51 1 n f
sfao 7 sfao 7
st1 |= ST1_SFAI; st1 |= ST1_SFAO;
sfma 2b 58 1 n f sfma 2a 58 1 n f
sfma 0 sfma 0
st1 = (st1 & ~ST1_SFMA) | (0 << ST1_SFMA_SHIFT); st1 = (st1 & ~ST1_SFMA) | (0 << ST1_SFMA_SHIFT);
sfma 2b 59 1 n f sfma 2a 59 1 n f
sfma 2 sfma 2
st1 = (st1 & ~ST1_SFMA) | (1 << ST1_SFMA_SHIFT); st1 = (st1 & ~ST1_SFMA) | (1 << ST1_SFMA_SHIFT);
sfma 2b 5a 1 n f sfma 2a 5a 1 n f
sfma 4 sfma 4
st1 = (st1 & ~ST1_SFMA) | (2 << ST1_SFMA_SHIFT); st1 = (st1 & ~ST1_SFMA) | (2 << ST1_SFMA_SHIFT);
sfma 2b 5b 1 n f sfma 2a 5b 1 n f
sfma -16 sfma -16
st1 = (st1 & ~ST1_SFMA) | (3 << ST1_SFMA_SHIFT); st1 = (st1 & ~ST1_SFMA) | (3 << ST1_SFMA_SHIFT);
@ -504,9 +511,12 @@ sub 1 0a 1 y
sub 1 0b 1 y sub 1 0b 1 y
sub %d,m sub %d,m
%sfai(d, %d);
%wa((int64_t)(int32_t)d - (%mo >> 16));
sub 1 0c 1 y sub 1 0c 1 y
sub %c,m sub %c,m
%wa((int64_t)(int32_t)%c - (%mo >> 16));
sub 1 0d 1 y sub 1 0d 1 y
sub %d,%c sub %d,%c
@ -535,3 +545,13 @@ zacc 1 10 1 n
zmac 1 30 1 n zmac 1 30 1 n
zmac zmac
raom 2a 3c 1 n
raom
/* Undocumented instruction, reset ALU saturation flag */
st1 &= ~ST1_AOVM;
saom 2a 3d 1 n
saom
/* Undocumented instruction, sets ALU saturation flag */
st1 |= ST1_AOVM;

View File

@ -23,11 +23,6 @@ TYPES = {
"f": None, "f": None,
} }
def expand_c(v):
fmt = ["%s", "(%s & 0xffff0000)", "(%s << 16)"][v["crm"]]
param = ["cmem[i->param]", "cmem[ca]"][v["cmode"]]
return fmt % param
def expand_d(v): def expand_d(v):
index = ["(i->param + ", "(id + "][v["dmode"]] index = ["(i->param + ", "(id + "][v["dmode"]]
mask = ["ba0) & 0xff] << 8)", "ba1) & 0x1f] << 8)"][v["dbp"]] mask = ["ba0) & 0xff] << 8)", "ba1) & 0x1f] << 8)"][v["dbp"]]
@ -44,6 +39,8 @@ def expand_mv(v):
c = ["", "s"][v["movm"]] c = ["", "s"][v["movm"]]
return "check_macc_overflow_%d%s()" % (v["sfmo"], c) return "check_macc_overflow_%d%s()" % (v["sfmo"], c)
EXPAND_C = ["get_cmem(i->param)", "get_cmem(ca)"]
EXPAND_WC = ["cmem[i->param] =", "cmem[ca] ="] EXPAND_WC = ["cmem[i->param] =", "cmem[ca] ="]
@ -70,14 +67,15 @@ def expand_wd1(v):
return "dmem%d[" % v["dbp"] + index + mask return "dmem%d[" % v["dbp"] + index + mask
WA2 = ( WA2 = (
" if(r < -0x80000000 || r > 0x7fffffff)\n" " if(r < -2147483648 || r > 2147483647) {\n"
" st1 |= ST1_AOV;\n" " st1 |= ST1_AOV;\n"
" if(st1 & ST1_AOVM) r = std::max(int64_t(-2147483648), std::min(int64_t(2147483647), r));\n"
" }"
" aacc = r;") " aacc = r;")
PDESC_EXPAND = { PDESC_EXPAND = {
"a": lambda v: ["aacc", "(aacc << 7)"][v["sfao"]], "a": lambda v: ["aacc", "(aacc << 7)"][v["sfao"]],
"c": expand_c, "c": lambda v: EXPAND_C[v["cmode"]],
"d": expand_d, "d": expand_d,
"d24": expand_d24, "d24": expand_d24,
"i": lambda v: "i->param", "i": lambda v: "i->param",
@ -97,7 +95,7 @@ PDESC_EXPAND = {
PDESC = { PDESC = {
"a": (0, ["sfao"]), "a": (0, ["sfao"]),
"c": (0, ["cmode", "crm"]), "c": (0, ["cmode"]),
"d": (0, ["dmode", "dbp"]), "d": (0, ["dmode", "dbp"]),
"d24": (0, ["dmode", "dbp"]), "d24": (0, ["dmode", "dbp"]),
"i": (0, []), "i": (0, []),
@ -115,7 +113,6 @@ VARIANTS = {
"cmode": (2, "xmode(opcode, 'c', cs)" ), "cmode": (2, "xmode(opcode, 'c', cs)" ),
"dmode": (2, "xmode(opcode, 'd', cs)" ), "dmode": (2, "xmode(opcode, 'd', cs)" ),
"sfai": (2, "sfai(st1)"), "sfai": (2, "sfai(st1)"),
"crm": (3, "crm(st1)"),
"dbp": (2, "dbp(st1)"), "dbp": (2, "dbp(st1)"),
"sfao": (2, "sfao(st1)"), "sfao": (2, "sfao(st1)"),
"sfmo": (4, "sfmo(st1)"), "sfmo": (4, "sfmo(st1)"),
@ -131,7 +128,6 @@ VARIANT_CANONICAL_ORDER = [
"cmode", "cmode",
"dmode", "dmode",
"sfai", "sfai",
"crm",
"dbp", "dbp",
"sfao", "sfao",
"sfmo", "sfmo",

View File

@ -6,9 +6,48 @@
Written by Olivier Galibert Written by Olivier Galibert
MAME conversion by R. Belmont MAME conversion by R. Belmont
Working emulation by The Talentuous Hands Of The Popularious hap Working emulation by The Talentuous Hands Of The Popularious hap
Improvements by superctr Properly working emulation by superctr
--------------------------------------------------------- ---------------------------------------------------------
Register map:
000-5fe : Channel specific registers
(high) (low)
+000 : xxxxxxxx -------- : Start address (low)
+000 : -------- xxxxxxxx : Unknown register (usually cleared)
+002 : xxxxxxxx -------- : Address page
: -------- xxxxxxxx : Start address (high)
+004 : -------- -------- : Unknown register (usually cleared)
+006 : -----x-- -------- : Unknown bit, always set
+008 : xxxxxxxx xxxxxxxx : Frequency
+00a : xxxxxxxx -------- : DSP ch 3 (right) output gain
: -------- xxxxxxxx : Loop address (low)
+00c : xxxxxxxx xxxxxxxx : End address
+00e : xxxxxxxx -------- : DSP ch 2 (Left) output gain
: -------- xxxxxxxx : Loop address (high)
+010 : xxxxxxxx xxxxxxxx : Filter time constant (latch)
+012 : -------- -------- : Unknown register (usually cleared)
+014 : xxxxxxxx xxxxxxxx : Volume (latch)
+016 : --x----- -------- : Key on status flag (only read)
+018 : xxxxxxxx xxxxxxxx : Filter time constant (Ramping target)
+01a : xxxxxxxx -------- : DSP ch 1 (chorus) output gain
: -------- xxxxxxxx : Filter ramping speed
+01c : xxxxxxxx xxxxxxxx : Volume (target)
+01e : xxxxxxxx -------- : DSP ch 0 (reverb) output gain
: -------- xxxxxxxx : Filter ramping speed
600-604 : Key on flags (each bit corresponds to a channel)
608-60c : Key off flags (each bit corresponds to a channel)
618 : Unknown register (usually 0x5cbc is written)
61a : Unknown register (usually 0x5cbc is written)
620 : Unknown register (usually 0x0128 is written)
628 : Unknown register (usually 0x0066 is written)
630 : Unknown register (usually 0x0001 is written)
638 : ROM readback address low
63a : ROM readback address high
63c : ROM readback word low
63e : ROM readback word high
---------------------------------------------------------
Additional notes on the sample format, reverse-engineered Additional notes on the sample format, reverse-engineered
by Olivier Galibert and David Haywood: by Olivier Galibert and David Haywood:
@ -45,12 +84,8 @@
--------------------------------------------------------- ---------------------------------------------------------
TODO: TODO:
- Filter behavior might not be perfect. - Filter and ramping behavior might not be perfect.
- Volume ramping probably behaves differently on hardware. - sometimes clicking
- hook up DSP, it's used for reverb and chorus effects.
- identify sample flags
* bassdrum in shikigam level 1 music is a good hint: it should be one octave
lower, indicating possible stereo sample, or base octave(like in ymf278)
- memory reads out of range sometimes - memory reads out of range sometimes
*/ */
@ -63,7 +98,6 @@ TODO:
#include <cmath> #include <cmath>
#define EMPHASIS_CUTOFF_BASE 0x800 #define EMPHASIS_CUTOFF_BASE 0x800
#define EMPHASIS_CUTOFF_SHIFT 1
#define EMPHASIS_OUTPUT_SHIFT 15 #define EMPHASIS_OUTPUT_SHIFT 15
// device type definition // device type definition
@ -104,16 +138,18 @@ void zsg2_device::device_start()
save_item(NAME(m_read_address)); save_item(NAME(m_read_address));
// Generate the output gain table. Assuming -1dB per step for now. // Generate the output gain table. Assuming -1dB per step for now.
for (int i = 0; i < 32; i++) for (int i = 0, history=0; i < 32; i++)
{ {
double val = pow(10, -(31 - i) / 20.) * 65535.; double val = pow(10, -(31 - i) / 20.) * 65535.;
gain_tab[i] = val; gain_tab[i] = val;
gain_tab_frac[i] = val-history;
history = val;
} }
for (int ch = 0; ch < 48; ch++) for (int ch = 0; ch < 48; ch++)
{ {
save_item(NAME(m_chan[ch].v), ch); save_item(NAME(m_chan[ch].v), ch);
save_item(NAME(m_chan[ch].is_playing), ch); save_item(NAME(m_chan[ch].status), ch);
save_item(NAME(m_chan[ch].cur_pos), ch); save_item(NAME(m_chan[ch].cur_pos), ch);
save_item(NAME(m_chan[ch].step_ptr), ch); save_item(NAME(m_chan[ch].step_ptr), ch);
save_item(NAME(m_chan[ch].step), ch); save_item(NAME(m_chan[ch].step), ch);
@ -125,14 +161,12 @@ void zsg2_device::device_start()
save_item(NAME(m_chan[ch].vol), ch); save_item(NAME(m_chan[ch].vol), ch);
save_item(NAME(m_chan[ch].vol_initial), ch); save_item(NAME(m_chan[ch].vol_initial), ch);
save_item(NAME(m_chan[ch].vol_target), ch); save_item(NAME(m_chan[ch].vol_target), ch);
save_item(NAME(m_chan[ch].vol_delta), ch);
save_item(NAME(m_chan[ch].emphasis_cutoff), ch);
save_item(NAME(m_chan[ch].emphasis_cutoff_initial), ch);
save_item(NAME(m_chan[ch].emphasis_cutoff_target), ch);
save_item(NAME(m_chan[ch].output_cutoff), ch); save_item(NAME(m_chan[ch].output_cutoff), ch);
save_item(NAME(m_chan[ch].output_cutoff_initial), ch); save_item(NAME(m_chan[ch].output_cutoff_initial), ch);
save_item(NAME(m_chan[ch].output_cutoff_target), ch); save_item(NAME(m_chan[ch].output_cutoff_target), ch);
save_item(NAME(m_chan[ch].output_cutoff_delta), ch);
save_item(NAME(m_chan[ch].emphasis_filter_state), ch); save_item(NAME(m_chan[ch].emphasis_filter_state), ch);
save_item(NAME(m_chan[ch].output_filter_state), ch); save_item(NAME(m_chan[ch].output_filter_state), ch);
@ -141,6 +175,8 @@ void zsg2_device::device_start()
save_item(NAME(m_chan[ch].samples), ch); save_item(NAME(m_chan[ch].samples), ch);
} }
save_item(NAME(m_sample_count));
} }
//------------------------------------------------- //-------------------------------------------------
@ -159,6 +195,7 @@ void zsg2_device::device_reset()
for (int ch = 0; ch < 48; ch++) for (int ch = 0; ch < 48; ch++)
for (int reg = 0; reg < 0x10; reg++) for (int reg = 0; reg < 0x10; reg++)
chan_w(ch, reg, 0); chan_w(ch, reg, 0);
m_sample_count = 0;
#if 0 #if 0
for (int i = 0; i < m_mem_blocks; i++) for (int i = 0; i < m_mem_blocks; i++)
@ -232,7 +269,7 @@ void zsg2_device::filter_samples(zchan *ch)
// not sure if the filter works exactly this way, however I am pleased // not sure if the filter works exactly this way, however I am pleased
// with the output for now. // with the output for now.
ch->emphasis_filter_state += (raw_samples[i]-(ch->emphasis_filter_state>>16)) * (EMPHASIS_CUTOFF_BASE - ch->emphasis_cutoff); ch->emphasis_filter_state += (raw_samples[i]-(ch->emphasis_filter_state>>16)) * EMPHASIS_CUTOFF_BASE;
ch->samples[i+1] = (ch->emphasis_filter_state) >> EMPHASIS_OUTPUT_SHIFT; ch->samples[i+1] = (ch->emphasis_filter_state) >> EMPHASIS_OUTPUT_SHIFT;
} }
} }
@ -243,6 +280,7 @@ void zsg2_device::filter_samples(zchan *ch)
void zsg2_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) void zsg2_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{ {
// DSP is programmed to expect 24-bit samples! So we're not limiting to 16-bit here
for (int i = 0; i < samples; i++) for (int i = 0; i < samples; i++)
{ {
int32_t mix[4] = {}; int32_t mix[4] = {};
@ -254,7 +292,7 @@ void zsg2_device::sound_stream_update(sound_stream &stream, stream_sample_t **in
//auto & elem = m_chan[0]; //auto & elem = m_chan[0];
{ {
ch++; ch++;
if (!elem.is_playing) if (!(elem.status & STATUS_ACTIVE))
continue; continue;
elem.step_ptr += elem.step; elem.step_ptr += elem.step;
@ -268,12 +306,11 @@ void zsg2_device::sound_stream_update(sound_stream &stream, stream_sample_t **in
if ((elem.cur_pos + 1) >= elem.end_pos) if ((elem.cur_pos + 1) >= elem.end_pos)
{ {
// end of sample // end of sample
elem.is_playing = false; elem.status &= ~STATUS_ACTIVE;
continue; continue;
} }
} }
filter_samples(&elem); filter_samples(&elem);
//elem.samples = prepare_samples(elem.page | elem.cur_pos);
} }
uint8_t sample_pos = elem.step_ptr >> 14 & 3; uint8_t sample_pos = elem.step_ptr >> 14 & 3;
@ -282,6 +319,7 @@ void zsg2_device::sound_stream_update(sound_stream &stream, stream_sample_t **in
// linear interpolation (hardware certainly does something similar) // linear interpolation (hardware certainly does something similar)
sample = elem.samples[sample_pos]; sample = elem.samples[sample_pos];
sample += ((uint16_t)(elem.step_ptr<<2&0xffff) * (int16_t)(elem.samples[sample_pos+1] - sample))>>16; sample += ((uint16_t)(elem.step_ptr<<2&0xffff) * (int16_t)(elem.samples[sample_pos+1] - sample))>>16;
sample = (sample * elem.vol) >> 16; sample = (sample * elem.vol) >> 16;
// another filter... // another filter...
@ -299,10 +337,13 @@ void zsg2_device::sound_stream_update(sound_stream &stream, stream_sample_t **in
mix[output] += (output_sample * gain_tab[output_gain&0x1f]) >> 13; mix[output] += (output_sample * gain_tab[output_gain&0x1f]) >> 13;
} }
// Apply transitions (This is not accurate yet) // Apply ramping every other update
elem.vol = ramp(elem.vol, elem.vol_target); // It's possible key on is handled on the other sample
elem.output_cutoff = ramp(elem.output_cutoff, elem.output_cutoff_target); if(m_sample_count & 1)
elem.emphasis_cutoff = ramp(elem.emphasis_cutoff, elem.emphasis_cutoff_target); {
elem.vol = ramp(elem.vol, elem.vol_target, elem.vol_delta);
elem.output_cutoff = ramp(elem.output_cutoff, elem.output_cutoff_target, elem.output_cutoff_delta);
}
} }
ch = 0; ch = 0;
@ -311,6 +352,7 @@ void zsg2_device::sound_stream_update(sound_stream &stream, stream_sample_t **in
outputs[output][i] = mix[output]; outputs[output][i] = mix[output];
} }
m_sample_count++;
} }
/******************************************************************************/ /******************************************************************************/
@ -347,9 +389,9 @@ void zsg2_device::chan_w(int ch, int reg, uint16_t data)
case 0x5: case 0x5:
// lo byte: loop address low // lo byte: loop address low
// hi byte: right output gain (bypass DSP) // hi byte: right output gain (direct)
m_chan[ch].loop_pos = (m_chan[ch].loop_pos & 0xff00) | (data & 0xff); m_chan[ch].loop_pos = (m_chan[ch].loop_pos & 0xff00) | (data & 0xff);
m_chan[ch].output_gain[1] = data >> 8; m_chan[ch].output_gain[3] = data >> 8;
break; break;
case 0x6: case 0x6:
@ -359,13 +401,13 @@ void zsg2_device::chan_w(int ch, int reg, uint16_t data)
case 0x7: case 0x7:
// lo byte: loop address high // lo byte: loop address high
// hi byte: left output gain (bypass DSP) // hi byte: left output gain (direct)
m_chan[ch].loop_pos = (m_chan[ch].loop_pos & 0x00ff) | (data << 8 & 0xff00); m_chan[ch].loop_pos = (m_chan[ch].loop_pos & 0x00ff) | (data << 8 & 0xff00);
m_chan[ch].output_gain[0] = data >> 8; m_chan[ch].output_gain[2] = data >> 8;
break; break;
case 0x8: case 0x8:
// Filter cutoff (Direct) // IIR lowpass time constant (initial, latched on key on)
m_chan[ch].output_cutoff_initial = data; m_chan[ch].output_cutoff_initial = data;
break; break;
@ -374,7 +416,7 @@ void zsg2_device::chan_w(int ch, int reg, uint16_t data)
break; break;
case 0xa: case 0xa:
// volume (Direct) // volume (initial, latched on key on)
m_chan[ch].vol_initial = data; m_chan[ch].vol_initial = data;
break; break;
@ -384,27 +426,27 @@ void zsg2_device::chan_w(int ch, int reg, uint16_t data)
break; break;
case 0xc: case 0xc:
// filter gain ? // IIR lowpass time constant (target)
m_chan[ch].output_cutoff_target = data; m_chan[ch].output_cutoff_target = data;
break; break;
case 0xd: case 0xd:
// hi byte: DSP Chorus volume // hi byte: DSP channel 1 (chorus) gain
// lo byte: Emphasis filter time constant (direct value) // lo byte: Filter ramping speed
m_chan[ch].output_gain[3] = data >> 8; m_chan[ch].output_gain[1] = data >> 8;
m_chan[ch].emphasis_cutoff_initial = expand_reg(data & 0xff); m_chan[ch].output_cutoff_delta = get_ramp(data & 0xff);
break; break;
case 0xe: case 0xe:
// volume (Target) // volume target
m_chan[ch].vol_target = data; m_chan[ch].vol_target = data;
break; break;
case 0xf: case 0xf:
// hi byte: DSP Reverb volume // hi byte: DSP channel 0 (reverb) gain
// lo byte: Emphasis filter time constant // lo byte: Volume ramping speed
m_chan[ch].output_gain[2] = data >> 8; m_chan[ch].output_gain[0] = data >> 8;
m_chan[ch].emphasis_cutoff_target = expand_reg(data & 0xff); m_chan[ch].vol_delta = get_ramp(data & 0xff);
break; break;
default: default:
@ -419,7 +461,7 @@ uint16_t zsg2_device::chan_r(int ch, int reg)
switch (reg) switch (reg)
{ {
case 0xb: // Only later games (taitogn) read this register... case 0xb: // Only later games (taitogn) read this register...
return m_chan[ch].is_playing << 13; return m_chan[ch].status;
default: default:
break; break;
} }
@ -427,37 +469,30 @@ uint16_t zsg2_device::chan_r(int ch, int reg)
return m_chan[ch].v[reg]; return m_chan[ch].v[reg];
} }
// expand 8-bit reg to 16-bit value. This is used for the emphasis filter // Convert ramping register value to something more usable.
// register. Not sure about how this works, the sound // Upper 4 bits is a shift amount, lower 4 bits is a 2's complement value.
// Get ramp amount by sign extending the low 4 bits, XOR by 8, then
// shifting it by the upper 4 bits.
// CPU uses a lookup table (stored in gdarius sound cpu ROM at 0x6332) to // CPU uses a lookup table (stored in gdarius sound cpu ROM at 0x6332) to
// calculate this value, for now I'm generating an opproximate inverse. // calculate this value, for now I'm generating an opproximate inverse.
int16_t zsg2_device::expand_reg(uint8_t val) int16_t zsg2_device::get_ramp(uint8_t val)
{ {
static const signed char frac_tab[16] = {8,9,10,11,12,13,14,15,-15,-14,-13,-12,-11,-10,-9,-8}; int16_t frac = val<<12; // sign extend
static const unsigned char shift_tab[8] = {1, 2, 3, 4, 5, 6, 7, 8}; frac = ((frac>>12) ^ 8) << (val >> 4);
return (frac_tab[val&0x0f] << shift_tab[val>>4])>>EMPHASIS_CUTOFF_SHIFT; return (frac >> 4);
} }
// ramp registers inline uint16_t zsg2_device::ramp(uint16_t current, uint16_t target, int16_t delta)
// The CPU does not write often enough to make the transitions always sound
// smooth, so the sound chip probably helps by smoothing the changes.
// There are two sets of the volume and filter cutoff registers.
// At key on, the CPU writes to the "direct" registers, after that it will
// write to the "target" register instead.
inline int32_t zsg2_device::ramp(int32_t current, int32_t target)
{ {
int32_t difference = abs(target-current); int32_t rampval = current + delta;
difference -= 0x40;
if(difference < 0) if(delta < 0 && rampval < target)
return target; rampval = target;
else if(target < current) else if(delta >= 0 && rampval > target)
return target + difference; rampval = target;
else if(target > current)
return target - difference; return rampval;
return target;
} }
/******************************************************************************/ /******************************************************************************/
@ -475,13 +510,12 @@ void zsg2_device::control_w(int reg, uint16_t data)
if (data & (1 << i)) if (data & (1 << i))
{ {
int ch = base | i; int ch = base | i;
m_chan[ch].is_playing = true; m_chan[ch].status |= STATUS_ACTIVE;
m_chan[ch].cur_pos = m_chan[ch].start_pos; m_chan[ch].cur_pos = m_chan[ch].start_pos;
m_chan[ch].step_ptr = 0; m_chan[ch].step_ptr = 0;
m_chan[ch].emphasis_filter_state = 0; m_chan[ch].emphasis_filter_state = 0;
m_chan[ch].vol = m_chan[ch].vol_initial; m_chan[ch].vol = m_chan[ch].vol_initial;
m_chan[ch].output_cutoff = m_chan[ch].output_cutoff_initial; m_chan[ch].output_cutoff = m_chan[ch].output_cutoff_initial;
m_chan[ch].emphasis_cutoff = m_chan[ch].emphasis_cutoff_initial;
filter_samples(&m_chan[ch]); filter_samples(&m_chan[ch]);
} }
} }
@ -497,7 +531,7 @@ void zsg2_device::control_w(int reg, uint16_t data)
if (data & (1 << i)) if (data & (1 << i))
{ {
int ch = base | i; int ch = base | i;
m_chan[ch].is_playing = false; m_chan[ch].status &= ~STATUS_ACTIVE;
} }
} }
break; break;

View File

@ -1,5 +1,5 @@
// license:BSD-3-Clause // license:BSD-3-Clause
// copyright-holders:Olivier Galibert, R. Belmont, hap // copyright-holders:Olivier Galibert, R. Belmont, hap, superctr
/* /*
ZOOM ZSG-2 custom wavetable synthesizer ZOOM ZSG-2 custom wavetable synthesizer
*/ */
@ -14,11 +14,6 @@
// INTERFACE CONFIGURATION MACROS // INTERFACE CONFIGURATION MACROS
//************************************************************************** //**************************************************************************
#define MCFG_ZSG2_ADD(_tag, _clock) \
MCFG_DEVICE_ADD(_tag, ZSG2, _clock)
#define MCFG_ZSG2_REPLACE(_tag, _clock) \
MCFG_DEVICE_REPLACE(_tag, ZSG2, _clock)
#define MCFG_ZSG2_EXT_READ_HANDLER(_devcb) \ #define MCFG_ZSG2_EXT_READ_HANDLER(_devcb) \
downcast<zsg2_device &>(*device).set_ext_read_handler(DEVCB_##_devcb); downcast<zsg2_device &>(*device).set_ext_read_handler(DEVCB_##_devcb);
@ -46,11 +41,14 @@ protected:
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
private: private:
const uint16_t STATUS_ACTIVE = 0x2000;
// 16 registers per channel, 48 channels // 16 registers per channel, 48 channels
struct zchan struct zchan
{ {
uint16_t v[16]; uint16_t v[16];
bool is_playing;
uint16_t status;
uint32_t cur_pos; uint32_t cur_pos;
uint32_t step_ptr; uint32_t step_ptr;
uint32_t step; uint32_t step;
@ -62,14 +60,12 @@ private:
uint16_t vol; uint16_t vol;
uint16_t vol_initial; uint16_t vol_initial;
uint16_t vol_target; uint16_t vol_target;
int16_t vol_delta;
int16_t emphasis_cutoff;
int16_t emphasis_cutoff_initial;
int16_t emphasis_cutoff_target;
uint16_t output_cutoff; uint16_t output_cutoff;
uint16_t output_cutoff_initial; uint16_t output_cutoff_initial;
uint16_t output_cutoff_target; uint16_t output_cutoff_target;
int16_t output_cutoff_delta;
int32_t emphasis_filter_state; int32_t emphasis_filter_state;
int32_t output_filter_state; int32_t output_filter_state;
@ -81,7 +77,10 @@ private:
}; };
uint16_t gain_tab[256]; uint16_t gain_tab[256];
uint16_t gain_tab_frac[256];
zchan m_chan[48]; zchan m_chan[48];
uint32_t m_sample_count;
required_region_ptr<uint32_t> m_mem_base; required_region_ptr<uint32_t> m_mem_base;
uint32_t m_read_address; uint32_t m_read_address;
@ -100,8 +99,8 @@ private:
uint16_t control_r(int reg); uint16_t control_r(int reg);
int16_t *prepare_samples(uint32_t offset); int16_t *prepare_samples(uint32_t offset);
void filter_samples(zchan *ch); void filter_samples(zchan *ch);
int16_t expand_reg(uint8_t val); int16_t get_ramp(uint8_t val);
inline int32_t ramp(int32_t current, int32_t target); inline uint16_t ramp(uint16_t current, uint16_t target, int16_t delta);
}; };
DECLARE_DEVICE_TYPE(ZSG2, zsg2_device) DECLARE_DEVICE_TYPE(ZSG2, zsg2_device)

View File

@ -22,7 +22,12 @@ and a Zoom Corp. ZFX-2 DSP instead of the TMS57002.
TODO: TODO:
- add DSP, sound is tinny without it - Raycrisis song 9 gets cut off due to clipping. Possible DSP emulation bug,
or just have to change the volumes. in the sound test, it will start to cut
at about 55%. and the timbre doesn't sound right anyway.
- check DSP behavior
- Implement the ramping control registers in zsg2.cpp
***************************************************************************/ ***************************************************************************/
@ -38,9 +43,11 @@ DEFINE_DEVICE_TYPE(TAITO_ZOOM, taito_zoom_device, "taito_zoom", "Taito Zoom Soun
// taito_zoom_device - constructor // taito_zoom_device - constructor
//------------------------------------------------- //-------------------------------------------------
taito_zoom_device::taito_zoom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) taito_zoom_device::taito_zoom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
: device_t(mconfig, TAITO_ZOOM, tag, owner, clock), device_t(mconfig, TAITO_ZOOM, tag, owner, clock),
device_mixer_interface(mconfig, *this, 2),
m_soundcpu(*this, "mn10200"), m_soundcpu(*this, "mn10200"),
m_tms57002(*this, "tms57002"),
m_zsg2(*this, "zsg2"), m_zsg2(*this, "zsg2"),
m_reg_address(0), m_reg_address(0),
m_tms_ctrl(0), m_tms_ctrl(0),
@ -71,6 +78,7 @@ void taito_zoom_device::device_reset()
m_reg_address = 0; m_reg_address = 0;
m_zsg2->reset(); m_zsg2->reset();
m_tms57002->reset();
} }
@ -98,15 +106,20 @@ READ8_MEMBER(taito_zoom_device::tms_ctrl_r)
WRITE8_MEMBER(taito_zoom_device::tms_ctrl_w) WRITE8_MEMBER(taito_zoom_device::tms_ctrl_w)
{ {
#if 0 // According to the TMS57002 manual, reset should NOT be set low during the data transfer.
tms57002_reset_w(data & 4); //m_tms57002->set_input_line(INPUT_LINE_RESET, data & 0x10 ? CLEAR_LINE : ASSERT_LINE);
tms57002_cload_w(data & 2); m_tms57002->cload_w(data & 2);
tms57002_pload_w(data & 1); m_tms57002->pload_w(data & 1);
#endif // Other bits unknown (0x9F at most games)
m_tms_ctrl = data; m_tms_ctrl = data;
} }
void taito_zoom_device::update_status_pin(int state)
{
printf("inside callback set status to %d\n",state);
m_soundcpu->set_input_line(1, state);
machine().scheduler().synchronize(); // the fix to all problems
}
void taito_zoom_device::taitozoom_mn_map(address_map &map) void taito_zoom_device::taitozoom_mn_map(address_map &map)
{ {
@ -116,11 +129,17 @@ void taito_zoom_device::taitozoom_mn_map(address_map &map)
map(0x080000, 0x0fffff).rom().region("mn10200", 0); map(0x080000, 0x0fffff).rom().region("mn10200", 0);
} }
map(0x400000, 0x41ffff).ram(); map(0x400000, 0x41ffff).ram();
map(0x800000, 0x8007ff).rw("zsg2", FUNC(zsg2_device::read), FUNC(zsg2_device::write)); map(0x800000, 0x8007ff).rw(m_zsg2, FUNC(zsg2_device::read), FUNC(zsg2_device::write));
map(0xc00000, 0xc00001).ram(); // TMS57002 comms map(0xc00000, 0xc00000).rw(m_tms57002, FUNC(tms57002_device::data_r), FUNC(tms57002_device::data_w)); // TMS57002 comms
map(0xe00000, 0xe000ff).rw(FUNC(taito_zoom_device::shared_ram_r), FUNC(taito_zoom_device::shared_ram_w)); // M66220FP for comms with maincpu map(0xe00000, 0xe000ff).rw(FUNC(taito_zoom_device::shared_ram_r), FUNC(taito_zoom_device::shared_ram_w)); // M66220FP for comms with maincpu
} }
#ifdef USE_DSP
void taito_zoom_device::tms57002_map(address_map &map)
{
map(0x00000, 0x3ffff).ram();
}
#endif
/*************************************************************************** /***************************************************************************
@ -148,14 +167,14 @@ WRITE16_MEMBER(taito_zoom_device::reg_data_w)
// zsg2+dsp global volume left // zsg2+dsp global volume left
if (data & 0xc0c0) if (data & 0xc0c0)
popmessage("ZOOM gain L %04X, contact MAMEdev", data); popmessage("ZOOM gain L %04X, contact MAMEdev", data);
m_zsg2->set_output_gain(0, (data & 0x3f) / 63.0); m_tms57002->set_output_gain(2, (data & 0x3f) / 63.0);
break; break;
case 0x05: case 0x05:
// zsg2+dsp global volume right // zsg2+dsp global volume right
if (data & 0xc0c0) if (data & 0xc0c0)
popmessage("ZOOM gain R %04X, contact MAMEdev", data); popmessage("ZOOM gain R %04X, contact MAMEdev", data);
m_zsg2->set_output_gain(1, (data & 0x3f) / 63.0); m_tms57002->set_output_gain(3, (data & 0x3f) / 63.0);
break; break;
default: default:
@ -184,13 +203,27 @@ MACHINE_CONFIG_START(taito_zoom_device::device_add_mconfig)
MCFG_QUANTUM_TIME(attotime::from_hz(60000)) MCFG_QUANTUM_TIME(attotime::from_hz(60000))
MCFG_ZSG2_ADD("zsg2", XTAL(25'000'000)) TMS57002(config, m_tms57002, XTAL(25'000'000)/2);
#ifdef USE_DSP
//m_tms57002->empty_callback().set_inputline(m_soundcpu, MN10200_IRQ1, m_tms57002->empty_r()); /*.invert();*/
m_tms57002->empty_callback().set_inputline(m_soundcpu, MN10200_IRQ1).invert();
// we assume the parent machine has created lspeaker/rspeaker m_tms57002->set_addrmap(AS_DATA, &taito_zoom_device::tms57002_map);
MCFG_SOUND_ROUTE(0, "^lspeaker", 1.0) // bypass DSP m_tms57002->add_route(2, *this, 1.0, AUTO_ALLOC_INPUT, 0);
MCFG_SOUND_ROUTE(1, "^rspeaker", 1.0) m_tms57002->add_route(3, *this, 1.0, AUTO_ALLOC_INPUT, 1);
#else // Unsupported opcode issue
m_tms57002->set_disable();
#endif
//MCFG_SOUND_ROUTE(2, "^lspeaker", 1.0) // DSP reverb ZSG2(config, m_zsg2, XTAL(25'000'000));
//MCFG_SOUND_ROUTE(3, "^rspeaker", 1.0) // DSP chorus #ifdef USE_DSP
m_zsg2->add_route(0, *m_tms57002, 0.5, 0); // reverb effect
m_zsg2->add_route(1, *m_tms57002, 0.5, 1); // chorus effect
m_zsg2->add_route(2, *m_tms57002, 0.5, 2); // left direct
m_zsg2->add_route(3, *m_tms57002, 0.5, 3); // right direct
#else
m_zsg2->add_route(2, *this, 1.0, AUTO_ALLOC_INPUT, 0);
m_zsg2->add_route(3, *this, 1.0, AUTO_ALLOC_INPUT, 1);
#endif
MACHINE_CONFIG_END MACHINE_CONFIG_END

View File

@ -14,12 +14,15 @@
#include "cpu/tms57002/tms57002.h" #include "cpu/tms57002/tms57002.h"
#include "sound/zsg2.h" #include "sound/zsg2.h"
#define USE_DSP // Uncomment when DSP emulation is working
class taito_zoom_device : public device_t class taito_zoom_device : public device_t, public device_mixer_interface
{ {
public: public:
taito_zoom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); static constexpr feature_type imperfect_features() { return feature::SOUND; }
taito_zoom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
DECLARE_WRITE16_MEMBER(sound_irq_w); DECLARE_WRITE16_MEMBER(sound_irq_w);
DECLARE_READ16_MEMBER(sound_irq_r); DECLARE_READ16_MEMBER(sound_irq_r);
@ -34,6 +37,9 @@ public:
void set_use_flash() { m_use_flash = true; } void set_use_flash() { m_use_flash = true; }
void taitozoom_mn_map(address_map &map); void taitozoom_mn_map(address_map &map);
#ifdef USE_DSP
void tms57002_map(address_map &map);
#endif
protected: protected:
// device-level overrides // device-level overrides
virtual void device_start() override; virtual void device_start() override;
@ -43,6 +49,7 @@ protected:
private: private:
// inherited devices/pointers // inherited devices/pointers
required_device<mn10200_device> m_soundcpu; required_device<mn10200_device> m_soundcpu;
required_device<tms57002_device> m_tms57002;
required_device<zsg2_device> m_zsg2; required_device<zsg2_device> m_zsg2;
// internal state // internal state
@ -50,6 +57,8 @@ private:
uint8_t m_tms_ctrl; uint8_t m_tms_ctrl;
bool m_use_flash; bool m_use_flash;
std::unique_ptr<uint8_t[]> m_snd_shared_ram; std::unique_ptr<uint8_t[]> m_snd_shared_ram;
void update_status_pin(int state);
}; };
DECLARE_DEVICE_TYPE(TAITO_ZOOM, taito_zoom_device) DECLARE_DEVICE_TYPE(TAITO_ZOOM, taito_zoom_device)

View File

@ -744,8 +744,10 @@ MACHINE_CONFIG_START(taitogn_state::coh3002t)
MCFG_SOUND_ROUTE(0, "lspeaker", 0.45) MCFG_SOUND_ROUTE(0, "lspeaker", 0.45)
MCFG_SOUND_ROUTE(1, "rspeaker", 0.45) MCFG_SOUND_ROUTE(1, "rspeaker", 0.45)
MCFG_TAITO_ZOOM_ADD("taito_zoom") TAITO_ZOOM(config, m_zoom);
MCFG_TAITO_ZOOM_USE_FLASH m_zoom->set_use_flash();
m_zoom->add_route(0, "lspeaker", 1.0);
m_zoom->add_route(1, "rspeaker", 1.0);
MCFG_DEVICE_MODIFY("taito_zoom:zsg2") MCFG_DEVICE_MODIFY("taito_zoom:zsg2")
MCFG_ZSG2_EXT_READ_HANDLER(READ32(*this, taitogn_state, zsg2_ext_r)) MCFG_ZSG2_EXT_READ_HANDLER(READ32(*this, taitogn_state, zsg2_ext_r))

View File

@ -1206,7 +1206,9 @@ MACHINE_CONFIG_START(zn_state::coh1000tb)
MCFG_SOUND_ROUTE(0, "lspeaker", 0.45) MCFG_SOUND_ROUTE(0, "lspeaker", 0.45)
MCFG_SOUND_ROUTE(1, "rspeaker", 0.45) MCFG_SOUND_ROUTE(1, "rspeaker", 0.45)
MCFG_TAITO_ZOOM_ADD("taito_zoom") TAITO_ZOOM(config, m_zoom);
m_zoom->add_route(0, "lspeaker", 1.0);
m_zoom->add_route(1, "rspeaker", 1.0);
MACHINE_CONFIG_END MACHINE_CONFIG_END
MACHINE_CONFIG_START(zn_state::coh1002tb) MACHINE_CONFIG_START(zn_state::coh1002tb)
@ -1228,7 +1230,9 @@ MACHINE_CONFIG_START(zn_state::coh1002tb)
MCFG_SOUND_ROUTE(0, "lspeaker", 0.45) MCFG_SOUND_ROUTE(0, "lspeaker", 0.45)
MCFG_SOUND_ROUTE(1, "rspeaker", 0.45) MCFG_SOUND_ROUTE(1, "rspeaker", 0.45)
MCFG_TAITO_ZOOM_ADD("taito_zoom") TAITO_ZOOM(config, m_zoom);
m_zoom->add_route(0, "lspeaker", 1.0);
m_zoom->add_route(1, "rspeaker", 1.0);
MACHINE_CONFIG_END MACHINE_CONFIG_END
/* /*