iremga20: assume keyon is at reg 6 bit 1(not just any non-0 value), removed 2nd end of sample check, misc refactor and added notes (nw)

This commit is contained in:
hap 2018-09-27 19:33:50 +02:00
parent cc68d98dd6
commit 3b454a7a0a
2 changed files with 60 additions and 82 deletions

View File

@ -3,8 +3,17 @@
/*********************************************************
Irem GA20 PCM Sound Chip
80 pin QFP, label NANAO GA20 (Nanao Corporation was Irem's parent company)
It's not currently known whether this chip is stereo.
TODO:
- It's not currently known whether this chip is stereo.
- Is sample position base(regs 0,1) used while sample is playing, or
latched at key on? We've always emulated it the latter way.
gunforc2 seems to be the only game updating the address regs sometimes
while a sample is playing, but it doesn't seem intentional.
- What is the 2nd sample address for? Is it end(cut-off) address, or
loop start address? Every game writes a value that's past sample end.
- All games write either 0 or 2 to reg #6, do other bits have any function?
Revisions:
@ -26,7 +35,7 @@ Revisions:
02-03-2007 R. Belmont
- Cleaned up faux x86 assembly.
09-25-2018 Valley Bell
09-25-2018 Valley Bell & co
- rewrote channel update to make data 0 act as sample terminator
*********************************************************/
@ -36,8 +45,6 @@ Revisions:
#include <algorithm>
#define MAX_VOL 256
// device type definition
DEFINE_DEVICE_TYPE(IREMGA20, iremga20_device, "iremga20", "Irem GA20")
@ -66,26 +73,16 @@ iremga20_device::iremga20_device(const machine_config &mconfig, const char *tag,
void iremga20_device::device_start()
{
int i;
iremga20_reset();
std::fill(std::begin(m_regs), std::end(m_regs), 0);
m_stream = stream_alloc(0, 2, clock()/4);
save_item(NAME(m_regs));
for (i = 0; i < 4; i++)
for (int i = 0; i < 4; i++)
{
save_item(NAME(m_channel[i].rate), i);
save_item(NAME(m_channel[i].size), i);
save_item(NAME(m_channel[i].start), i);
save_item(NAME(m_channel[i].pos), i);
save_item(NAME(m_channel[i].frac), i);
save_item(NAME(m_channel[i].end), i);
save_item(NAME(m_channel[i].volume), i);
save_item(NAME(m_channel[i].pan), i);
save_item(NAME(m_channel[i].effect), i);
save_item(NAME(m_channel[i].play), i);
}
}
@ -97,7 +94,16 @@ void iremga20_device::device_start()
void iremga20_device::device_reset()
{
iremga20_reset();
std::fill(std::begin(m_regs), std::end(m_regs), 0);
for (int i = 0; i < 4; i++)
{
m_channel[i].rate = 0;
m_channel[i].pos = 0;
m_channel[i].frac = 0;
m_channel[i].end = 0;
m_channel[i].volume = 0;
m_channel[i].play = 0;
}
}
//-------------------------------------------------
@ -142,12 +148,12 @@ void iremga20_device::sound_stream_update(sound_stream &stream, stream_sample_t
if (sample == 0x00) // check for sample end marker
ch.play = 0;
else
{
sampleout += (sample - 0x80) * (int32_t)ch.volume;
ch.frac += ch.rate;
ch.pos += (ch.frac >> 24);
ch.frac &= ((1 << 24) - 1);
if (ch.pos >= ch.end) // for safety (the actual chip probably doesn't check this)
ch.play = 0;
ch.frac += ch.rate;
ch.pos += (ch.frac >> 24);
ch.frac &= ((1 << 24) - 1);
}
}
}
@ -159,86 +165,64 @@ void iremga20_device::sound_stream_update(sound_stream &stream, stream_sample_t
WRITE8_MEMBER( iremga20_device::irem_ga20_w )
{
int channel;
//logerror("GA20: Offset %02x, data %04x\n",offset,data);
m_stream->update();
channel = offset >> 3;
offset &= 0x1f;
m_regs[offset] = data;
int ch = offset >> 3;
// channel regs:
// 0,1: start address
// 2,3: end? address
// 4: rate
// 5: volume
// 6: control
// 7: voice status (read-only)
switch (offset & 0x7)
{
case 0: /* start address low */
m_channel[channel].start = ((m_channel[channel].start)&0xff000) | (data<<4);
break;
case 1: /* start address high */
m_channel[channel].start = ((m_channel[channel].start)&0x00ff0) | (data<<12);
break;
case 2: /* end? address low */
m_channel[channel].end = ((m_channel[channel].end)&0xff000) | (data<<4);
break;
case 3: /* end? address high */
m_channel[channel].end = ((m_channel[channel].end)&0x00ff0) | (data<<12);
break;
case 4:
m_channel[channel].rate = (1 << 24) / (256 - data);
m_channel[ch].rate = (1 << 24) / (256 - data);
break;
case 5: //AT: gain control
m_channel[channel].volume = (data * MAX_VOL) / (data + 10);
case 5:
m_channel[ch].volume = (data * 256) / (data + 10);
break;
case 6: //AT: this is always written 2(enabling both channels?)
m_channel[channel].play = data;
m_channel[channel].pos = m_channel[channel].start;
m_channel[channel].frac = 0;
case 6:
// d1: key on/off
if (data & 2)
{
m_channel[ch].play = 1;
m_channel[ch].pos = (m_regs[ch << 3 | 0] | m_regs[ch << 3 | 1] << 8) << 4;
m_channel[ch].end = (m_regs[ch << 3 | 2] | m_regs[ch << 3 | 3] << 8) << 4;
m_channel[ch].frac = 0;
}
else
m_channel[ch].play = 0;
// other: unknown/unused
// possibilities are: loop flag, left/right speaker(stereo)
break;
}
}
READ8_MEMBER( iremga20_device::irem_ga20_r )
{
int channel;
m_stream->update();
channel = offset >> 3;
offset &= 0x1f;
int ch = offset >> 3;
switch (offset & 0x7)
{
case 7: // voice status. bit 0 is 1 if active. (routine around 0xccc in rtypeleo)
return m_channel[channel].play ? 1 : 0;
case 7: // voice status. bit 0 is 1 if active. (routine around 0xccc in rtypeleo)
return m_channel[ch].play;
default:
logerror("GA20: read unk. register %d, channel %d\n", offset & 0xf, channel);
logerror("GA20: read unk. register %d, channel %d\n", offset & 0x7, ch);
break;
}
return 0;
}
void iremga20_device::iremga20_reset()
{
int i;
for( i = 0; i < 4; i++ ) {
m_channel[i].rate = 0;
m_channel[i].size = 0;
m_channel[i].start = 0;
m_channel[i].pos = 0;
m_channel[i].frac = 0;
m_channel[i].end = 0;
m_channel[i].volume = 0;
m_channel[i].pan = 0;
m_channel[i].effect = 0;
m_channel[i].play = 0;
}
}

View File

@ -44,21 +44,15 @@ private:
struct channel_def
{
uint32_t rate;
uint32_t size;
uint32_t start;
uint32_t pos;
uint32_t frac;
uint32_t end;
uint32_t volume;
uint32_t pan;
uint32_t effect;
uint32_t play;
};
void iremga20_reset();
sound_stream *m_stream;
uint8_t m_regs[0x40/2];
uint8_t m_regs[0x20];
channel_def m_channel[4];
};