mirror of
https://github.com/holub/mame
synced 2025-07-04 17:38:08 +03:00
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:
parent
59c2a0536c
commit
3b57b7e90c
@ -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)
|
||||
: cpu_device(mconfig, TMS57002, tag, owner, clock)
|
||||
, 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_pc0_callback(*this)
|
||||
, m_empty_callback(*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)
|
||||
{
|
||||
@ -47,7 +49,9 @@ WRITE_LINE_MEMBER(tms57002_device::pload_w)
|
||||
if(olds ^ sti) {
|
||||
if (sti & IN_PLOAD) {
|
||||
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 (sti & IN_CLOAD) {
|
||||
hidx = 0;
|
||||
ca = 0;
|
||||
//ca = 0; // Seems extremely dubious
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
ca = 0;
|
||||
hidx = 0;
|
||||
id = 0;
|
||||
ba0 = 0;
|
||||
ba1 = 0;
|
||||
update_counter_tail = 0;
|
||||
update_counter_head = 0;
|
||||
st0 &= ~(ST0_INCS | ST0_DIRI | ST0_FI | ST0_SIM | ST0_PLRI |
|
||||
ST0_PBCI | ST0_DIRO | ST0_FO | ST0_SOM | ST0_PLRO |
|
||||
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);
|
||||
update_dready();
|
||||
update_pc0();
|
||||
update_empty();
|
||||
|
||||
xba = 0;
|
||||
xoa = 0;
|
||||
@ -112,6 +120,7 @@ WRITE8_MEMBER(tms57002_device::data_w)
|
||||
break;
|
||||
case SU_PRG:
|
||||
program->write_dword(pc++, val);
|
||||
update_pc0();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -121,9 +130,11 @@ WRITE8_MEMBER(tms57002_device::data_w)
|
||||
host[hidx++] = data;
|
||||
if(hidx >= 4) {
|
||||
uint32_t val = (host[0]<<24) | (host[1]<<16) | (host[2]<<8) | host[3];
|
||||
cmem[sa] = val;
|
||||
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 {
|
||||
sa = data;
|
||||
@ -160,11 +171,6 @@ READ8_MEMBER(tms57002_device::data_r)
|
||||
return res;
|
||||
}
|
||||
|
||||
READ_LINE_MEMBER(tms57002_device::empty_r)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
READ_LINE_MEMBER(tms57002_device::dready_r)
|
||||
{
|
||||
return sti & S_HOST ? 0 : 1;
|
||||
@ -180,9 +186,24 @@ READ_LINE_MEMBER(tms57002_device::pc0_r)
|
||||
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)
|
||||
{
|
||||
if(sti & (IN_PLOAD | IN_CLOAD))
|
||||
if(sti & (IN_PLOAD /*| IN_CLOAD*/))
|
||||
return;
|
||||
|
||||
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 m = macc;
|
||||
int64_t m = macc_read;
|
||||
uint64_t m1;
|
||||
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 m = macc;
|
||||
int64_t m = macc_read;
|
||||
uint64_t m1;
|
||||
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 m = macc;
|
||||
int64_t m = macc_read;
|
||||
uint64_t m1;
|
||||
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 m = macc;
|
||||
int64_t m = macc_read;
|
||||
uint64_t m1;
|
||||
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 m = macc;
|
||||
int64_t m = macc_read;
|
||||
uint64_t m1;
|
||||
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 m = macc;
|
||||
int64_t m = macc_read;
|
||||
uint64_t m1;
|
||||
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 m = macc;
|
||||
int64_t m = macc_read;
|
||||
uint64_t m1;
|
||||
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 m = macc;
|
||||
int64_t m = macc_read;
|
||||
uint64_t m1;
|
||||
int over = 0;
|
||||
|
||||
@ -607,6 +628,34 @@ int64_t tms57002_device::check_macc_overflow_3s()
|
||||
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()
|
||||
{
|
||||
int i;
|
||||
@ -730,7 +779,7 @@ void tms57002_device::execute_run()
|
||||
{
|
||||
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;
|
||||
|
||||
debugger_instruction_hook(pc);
|
||||
@ -746,6 +795,9 @@ void tms57002_device::execute_run()
|
||||
else
|
||||
xm_step_write();
|
||||
}
|
||||
|
||||
macc_read = macc_write;
|
||||
macc_write = macc;
|
||||
|
||||
for(;;) {
|
||||
uint32_t c, d;
|
||||
@ -786,8 +838,10 @@ void tms57002_device::execute_run()
|
||||
} else if(sti & S_BRANCH) {
|
||||
sti &= ~S_BRANCH;
|
||||
ipc = -1;
|
||||
} else
|
||||
} else {
|
||||
pc++; // Wraps if it reaches 256, next wraps too
|
||||
update_pc0();
|
||||
}
|
||||
|
||||
if(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()
|
||||
{
|
||||
m_dready_callback.resolve_safe();
|
||||
m_pc0_callback.resolve_safe();
|
||||
m_empty_callback.resolve_safe();
|
||||
}
|
||||
|
||||
void tms57002_device::device_start()
|
||||
@ -861,10 +917,13 @@ void tms57002_device::device_start()
|
||||
stream_alloc(4, 4, STREAM_SYNC);
|
||||
|
||||
save_item(NAME(macc));
|
||||
save_item(NAME(macc_read));
|
||||
save_item(NAME(macc_write));
|
||||
|
||||
save_item(NAME(cmem));
|
||||
save_item(NAME(dmem0));
|
||||
save_item(NAME(dmem1));
|
||||
save_item(NAME(update));
|
||||
|
||||
save_item(NAME(si));
|
||||
save_item(NAME(so));
|
||||
@ -893,6 +952,9 @@ void tms57002_device::device_start()
|
||||
|
||||
save_item(NAME(host));
|
||||
save_item(NAME(hidx));
|
||||
|
||||
save_item(NAME(update_counter_head));
|
||||
save_item(NAME(update_counter_tail));
|
||||
save_item(NAME(allow_update));
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,8 @@ public:
|
||||
tms57002_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
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_WRITE8_MEMBER(data_w);
|
||||
@ -51,7 +53,8 @@ private:
|
||||
S_READ = 0x00000040,
|
||||
S_WRITE = 0x00000080,
|
||||
S_BRANCH = 0x00000100,
|
||||
S_HOST = 0x00000200
|
||||
S_HOST = 0x00000200,
|
||||
S_UPDATE = 0x00000400,
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -75,6 +78,7 @@ private:
|
||||
ST1_AOV = 0x000001,
|
||||
ST1_SFAI = 0x000002,
|
||||
ST1_SFAO = 0x000004,
|
||||
ST1_AOVM = 0x000008, // undocumented!
|
||||
ST1_MOVM = 0x000020,
|
||||
ST1_MOV = 0x000040,
|
||||
ST1_SFMA = 0x000180, ST1_SFMA_SHIFT = 7,
|
||||
@ -120,7 +124,8 @@ private:
|
||||
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 dmem0[256];
|
||||
@ -136,10 +141,15 @@ private:
|
||||
uint32_t xm_adr;
|
||||
|
||||
uint8_t host[4], hidx, allow_update;
|
||||
|
||||
|
||||
uint32_t update[16];
|
||||
uint8_t update_counter_head, update_counter_tail;
|
||||
|
||||
cd cache;
|
||||
|
||||
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;
|
||||
|
||||
@ -164,6 +174,8 @@ private:
|
||||
inline int sfma(uint32_t st1);
|
||||
|
||||
void update_dready();
|
||||
void update_pc0();
|
||||
void update_empty();
|
||||
|
||||
void xm_init();
|
||||
void xm_step_read();
|
||||
@ -190,6 +202,7 @@ private:
|
||||
short get_hash(unsigned char adr, uint32_t st1, short *pnode);
|
||||
short get_hashnode(unsigned char adr, uint32_t st1, short pnode);
|
||||
int decode_get_pc();
|
||||
uint32_t get_cmem(uint8_t addr);
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "emu.h"
|
||||
#include "debugger.h"
|
||||
#include "tms57002.h"
|
||||
#include <algorithm>
|
||||
|
||||
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)
|
||||
{
|
||||
// value overridden during cvar update
|
||||
if(update_counter_head != update_counter_tail)
|
||||
return 0;
|
||||
|
||||
int crm = (st1 & ST1_CRM) >> ST1_CRM_SHIFT;
|
||||
return crm <= 2 ? crm : 0;
|
||||
}
|
||||
@ -90,6 +95,7 @@ void tms57002_device::decode_cat1(uint32_t opcode, unsigned short *op, cstate *c
|
||||
#undef CDEC1
|
||||
|
||||
default:
|
||||
logerror("Unhandled cat1 opcode %02x\n",opcode >> 18);
|
||||
decode_error(opcode);
|
||||
break;
|
||||
}
|
||||
@ -106,6 +112,7 @@ void tms57002_device::decode_cat2_pre(uint32_t opcode, unsigned short *op, cstat
|
||||
#undef CDEC2A
|
||||
|
||||
default:
|
||||
logerror("Unhandled cat2_pre opcode %02x \n",(opcode >> 11) & 0x7f);
|
||||
decode_error(opcode);
|
||||
break;
|
||||
}
|
||||
@ -122,6 +129,7 @@ void tms57002_device::decode_cat2_post(uint32_t opcode, unsigned short *op, csta
|
||||
#undef CDEC2B
|
||||
|
||||
default:
|
||||
logerror("Unhandled cat2_post opcode %02x\n",(opcode >> 11) & 0x7f);
|
||||
decode_error(opcode);
|
||||
break;
|
||||
}
|
||||
@ -138,6 +146,7 @@ void tms57002_device::decode_cat3(uint32_t opcode, unsigned short *op, cstate *c
|
||||
#undef CDEC3
|
||||
|
||||
default:
|
||||
logerror("Unhandled cat3 opcode %02x\n",(opcode >> 11) & 0x7f);
|
||||
decode_error(opcode);
|
||||
break;
|
||||
}
|
||||
|
@ -219,17 +219,17 @@ lirk 3 20 1 n
|
||||
|
||||
lmhc 1 33 1 n
|
||||
lmhc %c
|
||||
macc = ((int64_t)(int32_t)%c) << 16;
|
||||
macc_write = macc = ((int64_t)(int32_t)%c) << 16;
|
||||
|
||||
lmhd 1 31 1 n
|
||||
lmhd %d
|
||||
macc = ((int64_t)(int32_t)%d) << 16;
|
||||
macc_write = macc = ((int64_t)(int32_t)%d) << 16;
|
||||
|
||||
lmld 1 32 1 n
|
||||
lmld %d
|
||||
macc = (macc & ~0xffffffULL) | %d24;
|
||||
macc_write = macc = (macc & ~0xffffffULL) | %d24;
|
||||
|
||||
lpc 2b 31 1 n
|
||||
lpc 2a 31 1 n
|
||||
lpc %c
|
||||
if(sti & S_HOST)
|
||||
break;
|
||||
@ -242,7 +242,7 @@ lpc 2b 31 1 n
|
||||
sti |= S_HOST;
|
||||
update_dready();
|
||||
|
||||
lpd 2b 30 1 n
|
||||
lpd 2a 30 1 n
|
||||
lpd %d
|
||||
|
||||
mac 1 24 1 y
|
||||
@ -256,6 +256,12 @@ mac 1 24 1 y
|
||||
|
||||
mac 1 25 1 y
|
||||
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 %c,a
|
||||
@ -310,6 +316,7 @@ mpyu 1 28 1 y
|
||||
|
||||
neg 1 02 1 n
|
||||
neg
|
||||
%wa(-(int64_t)%a);
|
||||
|
||||
or 1 17 1 n
|
||||
or %d,a
|
||||
@ -408,11 +415,11 @@ scrm 2a 4b 1 n f
|
||||
scrm <3>
|
||||
st1 = (st1 & ~ST1_CRM) | (3 << ST1_CRM_SHIFT);
|
||||
|
||||
sfai 2b 54 1 n f
|
||||
sfai 2a 54 1 n f
|
||||
sfai 0
|
||||
st1 &= ~ST1_SFAI;
|
||||
|
||||
sfai 2b 55 1 n f
|
||||
sfai 2a 55 1 n f
|
||||
sfai -1
|
||||
st1 |= ST1_SFAI;
|
||||
|
||||
@ -422,21 +429,21 @@ sfao 2a 50 1 n f
|
||||
|
||||
sfao 2a 51 1 n f
|
||||
sfao 7
|
||||
st1 |= ST1_SFAI;
|
||||
st1 |= ST1_SFAO;
|
||||
|
||||
sfma 2b 58 1 n f
|
||||
sfma 2a 58 1 n f
|
||||
sfma 0
|
||||
st1 = (st1 & ~ST1_SFMA) | (0 << ST1_SFMA_SHIFT);
|
||||
|
||||
sfma 2b 59 1 n f
|
||||
sfma 2a 59 1 n f
|
||||
sfma 2
|
||||
st1 = (st1 & ~ST1_SFMA) | (1 << ST1_SFMA_SHIFT);
|
||||
|
||||
sfma 2b 5a 1 n f
|
||||
sfma 2a 5a 1 n f
|
||||
sfma 4
|
||||
st1 = (st1 & ~ST1_SFMA) | (2 << ST1_SFMA_SHIFT);
|
||||
|
||||
sfma 2b 5b 1 n f
|
||||
sfma 2a 5b 1 n f
|
||||
sfma -16
|
||||
st1 = (st1 & ~ST1_SFMA) | (3 << ST1_SFMA_SHIFT);
|
||||
|
||||
@ -504,9 +511,12 @@ sub 1 0a 1 y
|
||||
|
||||
sub 1 0b 1 y
|
||||
sub %d,m
|
||||
%sfai(d, %d);
|
||||
%wa((int64_t)(int32_t)d - (%mo >> 16));
|
||||
|
||||
sub 1 0c 1 y
|
||||
sub %c,m
|
||||
%wa((int64_t)(int32_t)%c - (%mo >> 16));
|
||||
|
||||
sub 1 0d 1 y
|
||||
sub %d,%c
|
||||
@ -535,3 +545,13 @@ zacc 1 10 1 n
|
||||
|
||||
zmac 1 30 1 n
|
||||
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;
|
||||
|
@ -23,11 +23,6 @@ TYPES = {
|
||||
"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):
|
||||
index = ["(i->param + ", "(id + "][v["dmode"]]
|
||||
mask = ["ba0) & 0xff] << 8)", "ba1) & 0x1f] << 8)"][v["dbp"]]
|
||||
@ -44,6 +39,8 @@ def expand_mv(v):
|
||||
c = ["", "s"][v["movm"]]
|
||||
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] ="]
|
||||
|
||||
|
||||
@ -70,14 +67,15 @@ def expand_wd1(v):
|
||||
return "dmem%d[" % v["dbp"] + index + mask
|
||||
|
||||
WA2 = (
|
||||
" if(r < -0x80000000 || r > 0x7fffffff)\n"
|
||||
" if(r < -2147483648 || r > 2147483647) {\n"
|
||||
" st1 |= ST1_AOV;\n"
|
||||
" if(st1 & ST1_AOVM) r = std::max(int64_t(-2147483648), std::min(int64_t(2147483647), r));\n"
|
||||
" }"
|
||||
" aacc = r;")
|
||||
|
||||
|
||||
PDESC_EXPAND = {
|
||||
"a": lambda v: ["aacc", "(aacc << 7)"][v["sfao"]],
|
||||
"c": expand_c,
|
||||
"c": lambda v: EXPAND_C[v["cmode"]],
|
||||
"d": expand_d,
|
||||
"d24": expand_d24,
|
||||
"i": lambda v: "i->param",
|
||||
@ -97,7 +95,7 @@ PDESC_EXPAND = {
|
||||
|
||||
PDESC = {
|
||||
"a": (0, ["sfao"]),
|
||||
"c": (0, ["cmode", "crm"]),
|
||||
"c": (0, ["cmode"]),
|
||||
"d": (0, ["dmode", "dbp"]),
|
||||
"d24": (0, ["dmode", "dbp"]),
|
||||
"i": (0, []),
|
||||
@ -115,7 +113,6 @@ VARIANTS = {
|
||||
"cmode": (2, "xmode(opcode, 'c', cs)" ),
|
||||
"dmode": (2, "xmode(opcode, 'd', cs)" ),
|
||||
"sfai": (2, "sfai(st1)"),
|
||||
"crm": (3, "crm(st1)"),
|
||||
"dbp": (2, "dbp(st1)"),
|
||||
"sfao": (2, "sfao(st1)"),
|
||||
"sfmo": (4, "sfmo(st1)"),
|
||||
@ -131,7 +128,6 @@ VARIANT_CANONICAL_ORDER = [
|
||||
"cmode",
|
||||
"dmode",
|
||||
"sfai",
|
||||
"crm",
|
||||
"dbp",
|
||||
"sfao",
|
||||
"sfmo",
|
||||
|
@ -6,9 +6,48 @@
|
||||
Written by Olivier Galibert
|
||||
MAME conversion by R. Belmont
|
||||
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
|
||||
by Olivier Galibert and David Haywood:
|
||||
|
||||
@ -45,12 +84,8 @@
|
||||
---------------------------------------------------------
|
||||
|
||||
TODO:
|
||||
- Filter behavior might not be perfect.
|
||||
- Volume ramping probably behaves differently on hardware.
|
||||
- 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)
|
||||
- Filter and ramping behavior might not be perfect.
|
||||
- sometimes clicking
|
||||
- memory reads out of range sometimes
|
||||
|
||||
*/
|
||||
@ -63,7 +98,6 @@ TODO:
|
||||
#include <cmath>
|
||||
|
||||
#define EMPHASIS_CUTOFF_BASE 0x800
|
||||
#define EMPHASIS_CUTOFF_SHIFT 1
|
||||
#define EMPHASIS_OUTPUT_SHIFT 15
|
||||
|
||||
// device type definition
|
||||
@ -104,16 +138,18 @@ void zsg2_device::device_start()
|
||||
save_item(NAME(m_read_address));
|
||||
|
||||
// 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.;
|
||||
gain_tab[i] = val;
|
||||
gain_tab_frac[i] = val-history;
|
||||
history = val;
|
||||
}
|
||||
|
||||
for (int ch = 0; ch < 48; 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].step_ptr), 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_initial), ch);
|
||||
save_item(NAME(m_chan[ch].vol_target), 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].vol_delta), 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_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].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_sample_count));
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
@ -159,6 +195,7 @@ void zsg2_device::device_reset()
|
||||
for (int ch = 0; ch < 48; ch++)
|
||||
for (int reg = 0; reg < 0x10; reg++)
|
||||
chan_w(ch, reg, 0);
|
||||
m_sample_count = 0;
|
||||
|
||||
#if 0
|
||||
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
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
{
|
||||
// DSP is programmed to expect 24-bit samples! So we're not limiting to 16-bit here
|
||||
for (int i = 0; i < samples; i++)
|
||||
{
|
||||
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];
|
||||
{
|
||||
ch++;
|
||||
if (!elem.is_playing)
|
||||
if (!(elem.status & STATUS_ACTIVE))
|
||||
continue;
|
||||
|
||||
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)
|
||||
{
|
||||
// end of sample
|
||||
elem.is_playing = false;
|
||||
elem.status &= ~STATUS_ACTIVE;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
filter_samples(&elem);
|
||||
//elem.samples = prepare_samples(elem.page | elem.cur_pos);
|
||||
}
|
||||
|
||||
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)
|
||||
sample = elem.samples[sample_pos];
|
||||
sample += ((uint16_t)(elem.step_ptr<<2&0xffff) * (int16_t)(elem.samples[sample_pos+1] - sample))>>16;
|
||||
|
||||
sample = (sample * elem.vol) >> 16;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Apply transitions (This is not accurate yet)
|
||||
elem.vol = ramp(elem.vol, elem.vol_target);
|
||||
elem.output_cutoff = ramp(elem.output_cutoff, elem.output_cutoff_target);
|
||||
elem.emphasis_cutoff = ramp(elem.emphasis_cutoff, elem.emphasis_cutoff_target);
|
||||
// Apply ramping every other update
|
||||
// It's possible key on is handled on the other sample
|
||||
if(m_sample_count & 1)
|
||||
{
|
||||
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;
|
||||
@ -311,6 +352,7 @@ void zsg2_device::sound_stream_update(sound_stream &stream, stream_sample_t **in
|
||||
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:
|
||||
// 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].output_gain[1] = data >> 8;
|
||||
m_chan[ch].output_gain[3] = data >> 8;
|
||||
break;
|
||||
|
||||
case 0x6:
|
||||
@ -359,13 +401,13 @@ void zsg2_device::chan_w(int ch, int reg, uint16_t data)
|
||||
|
||||
case 0x7:
|
||||
// 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].output_gain[0] = data >> 8;
|
||||
m_chan[ch].output_gain[2] = data >> 8;
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
// Filter cutoff (Direct)
|
||||
// IIR lowpass time constant (initial, latched on key on)
|
||||
m_chan[ch].output_cutoff_initial = data;
|
||||
break;
|
||||
|
||||
@ -374,7 +416,7 @@ void zsg2_device::chan_w(int ch, int reg, uint16_t data)
|
||||
break;
|
||||
|
||||
case 0xa:
|
||||
// volume (Direct)
|
||||
// volume (initial, latched on key on)
|
||||
m_chan[ch].vol_initial = data;
|
||||
break;
|
||||
|
||||
@ -384,27 +426,27 @@ void zsg2_device::chan_w(int ch, int reg, uint16_t data)
|
||||
break;
|
||||
|
||||
case 0xc:
|
||||
// filter gain ?
|
||||
// IIR lowpass time constant (target)
|
||||
m_chan[ch].output_cutoff_target = data;
|
||||
break;
|
||||
|
||||
case 0xd:
|
||||
// hi byte: DSP Chorus volume
|
||||
// lo byte: Emphasis filter time constant (direct value)
|
||||
m_chan[ch].output_gain[3] = data >> 8;
|
||||
m_chan[ch].emphasis_cutoff_initial = expand_reg(data & 0xff);
|
||||
// hi byte: DSP channel 1 (chorus) gain
|
||||
// lo byte: Filter ramping speed
|
||||
m_chan[ch].output_gain[1] = data >> 8;
|
||||
m_chan[ch].output_cutoff_delta = get_ramp(data & 0xff);
|
||||
break;
|
||||
|
||||
case 0xe:
|
||||
// volume (Target)
|
||||
// volume target
|
||||
m_chan[ch].vol_target = data;
|
||||
break;
|
||||
|
||||
case 0xf:
|
||||
// hi byte: DSP Reverb volume
|
||||
// lo byte: Emphasis filter time constant
|
||||
m_chan[ch].output_gain[2] = data >> 8;
|
||||
m_chan[ch].emphasis_cutoff_target = expand_reg(data & 0xff);
|
||||
// hi byte: DSP channel 0 (reverb) gain
|
||||
// lo byte: Volume ramping speed
|
||||
m_chan[ch].output_gain[0] = data >> 8;
|
||||
m_chan[ch].vol_delta = get_ramp(data & 0xff);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -419,7 +461,7 @@ uint16_t zsg2_device::chan_r(int ch, int reg)
|
||||
switch (reg)
|
||||
{
|
||||
case 0xb: // Only later games (taitogn) read this register...
|
||||
return m_chan[ch].is_playing << 13;
|
||||
return m_chan[ch].status;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -427,37 +469,30 @@ uint16_t zsg2_device::chan_r(int ch, int reg)
|
||||
return m_chan[ch].v[reg];
|
||||
}
|
||||
|
||||
// expand 8-bit reg to 16-bit value. This is used for the emphasis filter
|
||||
// register. Not sure about how this works, the sound
|
||||
// Convert ramping register value to something more usable.
|
||||
// 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
|
||||
// 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};
|
||||
static const unsigned char shift_tab[8] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
|
||||
return (frac_tab[val&0x0f] << shift_tab[val>>4])>>EMPHASIS_CUTOFF_SHIFT;
|
||||
int16_t frac = val<<12; // sign extend
|
||||
frac = ((frac>>12) ^ 8) << (val >> 4);
|
||||
|
||||
return (frac >> 4);
|
||||
}
|
||||
|
||||
// ramp registers
|
||||
// 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)
|
||||
inline uint16_t zsg2_device::ramp(uint16_t current, uint16_t target, int16_t delta)
|
||||
{
|
||||
int32_t difference = abs(target-current);
|
||||
difference -= 0x40;
|
||||
int32_t rampval = current + delta;
|
||||
|
||||
if(difference < 0)
|
||||
return target;
|
||||
else if(target < current)
|
||||
return target + difference;
|
||||
else if(target > current)
|
||||
return target - difference;
|
||||
|
||||
return target;
|
||||
if(delta < 0 && rampval < target)
|
||||
rampval = target;
|
||||
else if(delta >= 0 && rampval > target)
|
||||
rampval = target;
|
||||
|
||||
return rampval;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
@ -475,13 +510,12 @@ void zsg2_device::control_w(int reg, uint16_t data)
|
||||
if (data & (1 << 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].step_ptr = 0;
|
||||
m_chan[ch].emphasis_filter_state = 0;
|
||||
m_chan[ch].vol = m_chan[ch].vol_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]);
|
||||
}
|
||||
}
|
||||
@ -497,7 +531,7 @@ void zsg2_device::control_w(int reg, uint16_t data)
|
||||
if (data & (1 << i))
|
||||
{
|
||||
int ch = base | i;
|
||||
m_chan[ch].is_playing = false;
|
||||
m_chan[ch].status &= ~STATUS_ACTIVE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// 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
|
||||
*/
|
||||
@ -14,11 +14,6 @@
|
||||
// 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) \
|
||||
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;
|
||||
|
||||
private:
|
||||
const uint16_t STATUS_ACTIVE = 0x2000;
|
||||
|
||||
// 16 registers per channel, 48 channels
|
||||
struct zchan
|
||||
{
|
||||
uint16_t v[16];
|
||||
bool is_playing;
|
||||
|
||||
uint16_t status;
|
||||
uint32_t cur_pos;
|
||||
uint32_t step_ptr;
|
||||
uint32_t step;
|
||||
@ -62,14 +60,12 @@ private:
|
||||
uint16_t vol;
|
||||
uint16_t vol_initial;
|
||||
uint16_t vol_target;
|
||||
|
||||
int16_t emphasis_cutoff;
|
||||
int16_t emphasis_cutoff_initial;
|
||||
int16_t emphasis_cutoff_target;
|
||||
int16_t vol_delta;
|
||||
|
||||
uint16_t output_cutoff;
|
||||
uint16_t output_cutoff_initial;
|
||||
uint16_t output_cutoff_target;
|
||||
int16_t output_cutoff_delta;
|
||||
|
||||
int32_t emphasis_filter_state;
|
||||
int32_t output_filter_state;
|
||||
@ -81,7 +77,10 @@ private:
|
||||
};
|
||||
|
||||
uint16_t gain_tab[256];
|
||||
uint16_t gain_tab_frac[256];
|
||||
|
||||
zchan m_chan[48];
|
||||
uint32_t m_sample_count;
|
||||
|
||||
required_region_ptr<uint32_t> m_mem_base;
|
||||
uint32_t m_read_address;
|
||||
@ -100,8 +99,8 @@ private:
|
||||
uint16_t control_r(int reg);
|
||||
int16_t *prepare_samples(uint32_t offset);
|
||||
void filter_samples(zchan *ch);
|
||||
int16_t expand_reg(uint8_t val);
|
||||
inline int32_t ramp(int32_t current, int32_t target);
|
||||
int16_t get_ramp(uint8_t val);
|
||||
inline uint16_t ramp(uint16_t current, uint16_t target, int16_t delta);
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(ZSG2, zsg2_device)
|
||||
|
@ -22,7 +22,12 @@ and a Zoom Corp. ZFX-2 DSP instead of the TMS57002.
|
||||
|
||||
|
||||
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::taito_zoom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, TAITO_ZOOM, tag, owner, 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_mixer_interface(mconfig, *this, 2),
|
||||
m_soundcpu(*this, "mn10200"),
|
||||
m_tms57002(*this, "tms57002"),
|
||||
m_zsg2(*this, "zsg2"),
|
||||
m_reg_address(0),
|
||||
m_tms_ctrl(0),
|
||||
@ -71,6 +78,7 @@ void taito_zoom_device::device_reset()
|
||||
m_reg_address = 0;
|
||||
|
||||
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)
|
||||
{
|
||||
#if 0
|
||||
tms57002_reset_w(data & 4);
|
||||
tms57002_cload_w(data & 2);
|
||||
tms57002_pload_w(data & 1);
|
||||
#endif
|
||||
|
||||
// According to the TMS57002 manual, reset should NOT be set low during the data transfer.
|
||||
//m_tms57002->set_input_line(INPUT_LINE_RESET, data & 0x10 ? CLEAR_LINE : ASSERT_LINE);
|
||||
m_tms57002->cload_w(data & 2);
|
||||
m_tms57002->pload_w(data & 1);
|
||||
// Other bits unknown (0x9F at most games)
|
||||
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)
|
||||
{
|
||||
@ -116,11 +129,17 @@ void taito_zoom_device::taitozoom_mn_map(address_map &map)
|
||||
map(0x080000, 0x0fffff).rom().region("mn10200", 0);
|
||||
}
|
||||
map(0x400000, 0x41ffff).ram();
|
||||
map(0x800000, 0x8007ff).rw("zsg2", FUNC(zsg2_device::read), FUNC(zsg2_device::write));
|
||||
map(0xc00000, 0xc00001).ram(); // TMS57002 comms
|
||||
map(0x800000, 0x8007ff).rw(m_zsg2, FUNC(zsg2_device::read), FUNC(zsg2_device::write));
|
||||
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
|
||||
}
|
||||
|
||||
#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
|
||||
if (data & 0xc0c0)
|
||||
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;
|
||||
|
||||
case 0x05:
|
||||
// zsg2+dsp global volume right
|
||||
if (data & 0xc0c0)
|
||||
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;
|
||||
|
||||
default:
|
||||
@ -184,13 +203,27 @@ MACHINE_CONFIG_START(taito_zoom_device::device_add_mconfig)
|
||||
|
||||
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
|
||||
MCFG_SOUND_ROUTE(0, "^lspeaker", 1.0) // bypass DSP
|
||||
MCFG_SOUND_ROUTE(1, "^rspeaker", 1.0)
|
||||
m_tms57002->set_addrmap(AS_DATA, &taito_zoom_device::tms57002_map);
|
||||
m_tms57002->add_route(2, *this, 1.0, AUTO_ALLOC_INPUT, 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
|
||||
//MCFG_SOUND_ROUTE(3, "^rspeaker", 1.0) // DSP chorus
|
||||
ZSG2(config, m_zsg2, XTAL(25'000'000));
|
||||
#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
|
||||
|
@ -14,12 +14,15 @@
|
||||
#include "cpu/tms57002/tms57002.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:
|
||||
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_READ16_MEMBER(sound_irq_r);
|
||||
@ -34,6 +37,9 @@ public:
|
||||
void set_use_flash() { m_use_flash = true; }
|
||||
|
||||
void taitozoom_mn_map(address_map &map);
|
||||
#ifdef USE_DSP
|
||||
void tms57002_map(address_map &map);
|
||||
#endif
|
||||
protected:
|
||||
// device-level overrides
|
||||
virtual void device_start() override;
|
||||
@ -43,6 +49,7 @@ protected:
|
||||
private:
|
||||
// inherited devices/pointers
|
||||
required_device<mn10200_device> m_soundcpu;
|
||||
required_device<tms57002_device> m_tms57002;
|
||||
required_device<zsg2_device> m_zsg2;
|
||||
|
||||
// internal state
|
||||
@ -50,6 +57,8 @@ private:
|
||||
uint8_t m_tms_ctrl;
|
||||
bool m_use_flash;
|
||||
std::unique_ptr<uint8_t[]> m_snd_shared_ram;
|
||||
|
||||
void update_status_pin(int state);
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(TAITO_ZOOM, taito_zoom_device)
|
||||
|
@ -744,8 +744,10 @@ MACHINE_CONFIG_START(taitogn_state::coh3002t)
|
||||
MCFG_SOUND_ROUTE(0, "lspeaker", 0.45)
|
||||
MCFG_SOUND_ROUTE(1, "rspeaker", 0.45)
|
||||
|
||||
MCFG_TAITO_ZOOM_ADD("taito_zoom")
|
||||
MCFG_TAITO_ZOOM_USE_FLASH
|
||||
TAITO_ZOOM(config, m_zoom);
|
||||
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_ZSG2_EXT_READ_HANDLER(READ32(*this, taitogn_state, zsg2_ext_r))
|
||||
|
@ -1206,7 +1206,9 @@ MACHINE_CONFIG_START(zn_state::coh1000tb)
|
||||
MCFG_SOUND_ROUTE(0, "lspeaker", 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_START(zn_state::coh1002tb)
|
||||
@ -1228,7 +1230,9 @@ MACHINE_CONFIG_START(zn_state::coh1002tb)
|
||||
MCFG_SOUND_ROUTE(0, "lspeaker", 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
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user