Compare commits

...

5 Commits

Author SHA1 Message Date
MetalliC bd69d42a02 pgm2: a bit less awful hack (nw) 2017-11-10 20:18:57 +02:00
MetalliC 41e94b4c12 arm7: add ARM v5/9 check (nw) 2017-11-10 20:18:09 +02:00
MetalliC bb96e249ca ymz770.cpp: 2nd volume control, notes (nw) 2017-11-10 20:16:50 +02:00
AJR a2be3de956 qtsbc: Add notes (nw) 2017-11-10 13:12:38 -05:00
hap fee21b0a91 ymz770: renamed "elem" for readability (nw) 2017-11-10 18:38:26 +01:00
5 changed files with 132 additions and 98 deletions

View File

@ -492,8 +492,7 @@ void arm7_cpu_device::HandleMemSingle(uint32_t insn)
R15 = (R15 & ~0x03FFFFFC) /* N Z C V I F M1 M0 */ | ((data - 4) & 0x03FFFFFC); R15 = (R15 & ~0x03FFFFFC) /* N Z C V I F M1 M0 */ | ((data - 4) & 0x03FFFFFC);
// LDR, PC takes 2S + 2N + 1I (5 total cycles) // LDR, PC takes 2S + 2N + 1I (5 total cycles)
ARM7_ICOUNT -= 2; ARM7_ICOUNT -= 2;
// TODO only ARMv5T and above if ((data & 1) && m_archRev >= 5)
if (data & 1)
{ {
set_cpsr(GET_CPSR | T_MASK); set_cpsr(GET_CPSR | T_MASK);
R15--; R15--;
@ -1377,9 +1376,8 @@ void arm7_cpu_device::HandleMemBlock(uint32_t insn)
SwitchMode(temp & 3); SwitchMode(temp & 3);
} }
} }
// TODO only ARMv5T and above
else else
if (R15 & 1) if ((R15 & 1) && m_archRev >= 5)
{ {
set_cpsr(GET_CPSR | T_MASK); set_cpsr(GET_CPSR | T_MASK);
R15--; R15--;
@ -1441,9 +1439,8 @@ void arm7_cpu_device::HandleMemBlock(uint32_t insn)
SwitchMode(temp & 3); SwitchMode(temp & 3);
} }
} }
// TODO only ARMv5T and above
else else
if (R15 & 1) if ((R15 & 1) && m_archRev >= 5)
{ {
set_cpsr(GET_CPSR | T_MASK); set_cpsr(GET_CPSR | T_MASK);
R15--; R15--;

View File

@ -2,20 +2,22 @@
// copyright-holders:Olivier Galibert, R. Belmont, MetalliC // copyright-holders:Olivier Galibert, R. Belmont, MetalliC
/*************************************************************************** /***************************************************************************
ymz770.c Yamaha YMZ770C and YMZ774
Emulation by R. Belmont and MetalliC Emulation by R. Belmont and MetalliC
AMM decode by Olivier Galibert AMM decode by Olivier Galibert
----- -----
TODO: TODO:
- A lot of unimplemented features, even simple ones like panning,
these should be added once we find out any software that uses it.
- Sequencer is very preliminary
- verify if pan 100% correct
- What does channel ATBL mean? - What does channel ATBL mean?
- Is YMZ774(and other variants) the same family as this chip? 770:
What are the differences? - verify if pan 100% correct
- sequencer timers and triggers not implemented (seems used in Deathsmiles ending tune)
774:
- find out how volume/pan delayed transition works (used few times in orleg2 attract mode)
- 4 channel output
- Equalizer
- Sequencer (not used)
***************************************************************************/ ***************************************************************************/
@ -45,6 +47,7 @@ ymz770_device::ymz770_device(const machine_config &mconfig, device_type type, co
, m_mute(0) , m_mute(0)
, m_doen(0) , m_doen(0)
, m_vlma(0) , m_vlma(0)
, m_vlma1(0)
, m_bsl(0) , m_bsl(0)
, m_cpl(0) , m_cpl(0)
, m_rom(*this, DEVICE_SELF) , m_rom(*this, DEVICE_SELF)
@ -61,19 +64,20 @@ void ymz770_device::device_start()
// create the stream // create the stream
m_stream = machine().sound().stream_alloc(*this, 0, 2, m_sclock); m_stream = machine().sound().stream_alloc(*this, 0, 2, m_sclock);
for (auto & elem : m_channels) for (auto & channel : m_channels)
{ {
elem.is_playing = false; channel.is_playing = false;
elem.decoder = new mpeg_audio(&m_rom[0], mpeg_audio::AMM, false, 0); channel.decoder = new mpeg_audio(&m_rom[0], mpeg_audio::AMM, false, 0);
} }
for (auto & elem : m_sequences) for (auto & sequence : m_sequences)
elem.is_seq_playing = false; sequence.is_playing = false;
// register for save states // register for save states
save_item(NAME(m_cur_reg)); save_item(NAME(m_cur_reg));
save_item(NAME(m_mute)); save_item(NAME(m_mute));
save_item(NAME(m_doen)); save_item(NAME(m_doen));
save_item(NAME(m_vlma)); save_item(NAME(m_vlma));
save_item(NAME(m_vlma1));
save_item(NAME(m_bsl)); save_item(NAME(m_bsl));
save_item(NAME(m_cpl)); save_item(NAME(m_cpl));
@ -82,6 +86,8 @@ void ymz770_device::device_start()
save_item(NAME(m_channels[ch].phrase), ch); save_item(NAME(m_channels[ch].phrase), ch);
save_item(NAME(m_channels[ch].pan), ch); save_item(NAME(m_channels[ch].pan), ch);
save_item(NAME(m_channels[ch].pan_delay), ch); save_item(NAME(m_channels[ch].pan_delay), ch);
save_item(NAME(m_channels[ch].pan1), ch);
save_item(NAME(m_channels[ch].pan1_delay), ch);
save_item(NAME(m_channels[ch].volume), ch); save_item(NAME(m_channels[ch].volume), ch);
save_item(NAME(m_channels[ch].volume_delay), ch); save_item(NAME(m_channels[ch].volume_delay), ch);
save_item(NAME(m_channels[ch].volume2), ch); save_item(NAME(m_channels[ch].volume2), ch);
@ -97,9 +103,9 @@ void ymz770_device::device_start()
for (int ch = 0; ch < 8; ch++) for (int ch = 0; ch < 8; ch++)
{ {
save_item(NAME(m_sequences[ch].sequence), ch); save_item(NAME(m_sequences[ch].sequence), ch);
save_item(NAME(m_sequences[ch].seqcontrol), ch); save_item(NAME(m_sequences[ch].control), ch);
save_item(NAME(m_sequences[ch].seqdelay), ch); save_item(NAME(m_sequences[ch].delay), ch);
save_item(NAME(m_sequences[ch].is_seq_playing), ch); save_item(NAME(m_sequences[ch].is_playing), ch);
} }
} }
@ -110,25 +116,27 @@ void ymz770_device::device_start()
void ymz770_device::device_reset() void ymz770_device::device_reset()
{ {
for (auto & elem : m_channels) for (auto & channel : m_channels)
{ {
elem.phrase = 0; channel.phrase = 0;
elem.pan = 64; channel.pan = 64;
elem.pan_delay = 0; channel.pan_delay = 0;
elem.volume = 0; channel.pan1 = 64;
elem.volume_delay = 0; channel.pan1_delay = 0;
elem.volume2 = 0; channel.volume = 0;
elem.loop = 0; channel.volume_delay = 0;
elem.is_playing = false; channel.volume2 = 0;
elem.output_remaining = 0; channel.loop = 0;
elem.decoder->clear(); channel.is_playing = false;
channel.output_remaining = 0;
channel.decoder->clear();
} }
for (auto & elem : m_sequences) for (auto & sequence : m_sequences)
{ {
elem.sequence = 0; sequence.sequence = 0;
elem.seqcontrol = 0; sequence.control = 0;
elem.seqdelay = 0; sequence.delay = 0;
elem.is_seq_playing = false; sequence.is_playing = false;
} }
} }
@ -153,61 +161,63 @@ void ymz770_device::sound_stream_update(sound_stream &stream, stream_sample_t **
int32_t mixl = 0; int32_t mixl = 0;
int32_t mixr = 0; int32_t mixr = 0;
for (auto & elem : m_channels) for (auto & channel : m_channels)
{ {
if (elem.output_remaining > 0) if (channel.output_remaining > 0)
{ {
// force finish current block // force finish current block
int32_t smpl = elem.output_data[elem.output_ptr++] * elem.volume; // volume is linear, 0 - 128 (100%) int32_t smpl = (channel.output_data[channel.output_ptr++] * channel.volume) >> 7; // volume is linear, 0 - 128 (100%)
mixr += (smpl * elem.pan) >> 14; // pan seems linear, 0 - 128, where 0 = 100% left, 128 = 100% right, 64 = 50% left 50% right smpl = (smpl * channel.volume2) >> 7;
mixl += (smpl * (128 - elem.pan)) >> 14; mixr += (smpl * channel.pan) >> 7; // pan seems linear, 0 - 128, where 0 = 100% left, 128 = 100% right, 64 = 50% left 50% right
elem.output_remaining--; mixl += (smpl * (128 - channel.pan)) >> 7;
channel.output_remaining--;
if (elem.output_remaining == 0 && !elem.is_playing) if (channel.output_remaining == 0 && !channel.is_playing)
elem.decoder->clear(); channel.decoder->clear();
} }
else if (elem.is_playing) else if (channel.is_playing)
{ {
retry: retry:
if (elem.last_block) if (channel.last_block)
{ {
if (elem.loop) if (channel.loop)
{ {
if (elem.loop != 255) if (channel.loop != 255)
--elem.loop; --channel.loop;
// loop sample // loop sample
int phrase = elem.phrase; int phrase = channel.phrase;
elem.atbl = m_rom[(4*phrase)+0] >> 4 & 7; channel.atbl = m_rom[(4*phrase)+0] >> 4 & 7;
elem.pptr = 8 * get_phrase_offs(phrase); channel.pptr = 8 * get_phrase_offs(phrase);
} }
else else
{ {
elem.is_playing = false; channel.is_playing = false;
elem.output_remaining = 0; channel.output_remaining = 0;
elem.decoder->clear(); channel.decoder->clear();
} }
} }
if (elem.is_playing) if (channel.is_playing)
{ {
// next block // next block
int sample_rate, channel_count; int sample_rate, channel_count;
if (!elem.decoder->decode_buffer(elem.pptr, m_rom.bytes()*8, elem.output_data, elem.output_remaining, sample_rate, channel_count) || elem.output_remaining == 0) if (!channel.decoder->decode_buffer(channel.pptr, m_rom.bytes()*8, channel.output_data, channel.output_remaining, sample_rate, channel_count) || channel.output_remaining == 0)
{ {
elem.is_playing = !elem.last_block; // detect infinite retry loop channel.is_playing = !channel.last_block; // detect infinite retry loop
elem.last_block = true; channel.last_block = true;
elem.output_remaining = 0; channel.output_remaining = 0;
goto retry; goto retry;
} }
elem.last_block = elem.output_remaining < 1152; channel.last_block = channel.output_remaining < 1152;
elem.output_remaining--; channel.output_remaining--;
elem.output_ptr = 1; channel.output_ptr = 1;
int32_t smpl = elem.output_data[0] * elem.volume; int32_t smpl = (channel.output_data[0] * channel.volume) >> 7;
mixr += (smpl * elem.pan) >> 14; smpl = (smpl * channel.volume2) >> 7;
mixl += (smpl * (128 - elem.pan)) >> 14; mixr += (smpl * channel.pan) >> 7;
mixl += (smpl * (128 - channel.pan)) >> 7;
} }
} }
} }
@ -243,35 +253,35 @@ retry:
void ymz770_device::sequencer() void ymz770_device::sequencer()
{ {
for (auto & elem : m_sequences) for (auto & sequence : m_sequences)
{ {
if (elem.is_seq_playing) if (sequence.is_playing)
{ {
if (elem.seqdelay > 0) if (sequence.delay > 0)
{ {
elem.seqdelay--; sequence.delay--;
} }
else else
{ {
int reg = *elem.seqdata++; int reg = *sequence.data++;
uint8_t data = *elem.seqdata++; uint8_t data = *sequence.data++;
switch (reg) switch (reg)
{ {
case 0x0f: case 0x0f:
if (elem.seqcontrol & 1) if (sequence.control & 1)
{ {
// loop sequence // loop sequence
uint8_t sqn = elem.sequence; uint8_t sqn = sequence.sequence;
uint32_t pptr = get_seq_offs(sqn); uint32_t pptr = get_seq_offs(sqn);
elem.seqdata = &m_rom[pptr]; sequence.data = &m_rom[pptr];
} }
else else
{ {
elem.is_seq_playing = false; sequence.is_playing = false;
} }
break; break;
case 0x0e: case 0x0e:
elem.seqdelay = 32 - 1; sequence.delay = 32 - 1;
break; break;
default: default:
internal_reg_write(reg, data); internal_reg_write(reg, data);
@ -340,6 +350,7 @@ void ymz770_device::internal_reg_write(uint8_t reg, uint8_t data)
case 1: case 1:
m_channels[ch].volume = data; m_channels[ch].volume = data;
m_channels[ch].volume2 = 128;
break; break;
case 2: case 2:
@ -381,16 +392,16 @@ void ymz770_device::internal_reg_write(uint8_t reg, uint8_t data)
{ {
uint8_t sqn = m_sequences[ch].sequence; uint8_t sqn = m_sequences[ch].sequence;
uint32_t pptr = get_seq_offs(sqn); uint32_t pptr = get_seq_offs(sqn);
m_sequences[ch].seqdata = &m_rom[pptr]; m_sequences[ch].data = &m_rom[pptr];
m_sequences[ch].seqdelay = 0; m_sequences[ch].delay = 0;
m_sequences[ch].is_seq_playing = true; m_sequences[ch].is_playing = true;
} }
else else
{ {
m_sequences[ch].is_seq_playing = false; m_sequences[ch].is_playing = false;
} }
m_sequences[ch].seqcontrol = data; m_sequences[ch].control = data;
break; break;
default: default:
@ -460,6 +471,13 @@ void ymz774_device::internal_reg_write(uint8_t reg, uint8_t data)
if (data) logerror("unimplemented write %02X %02X\n", reg, data); if (data) logerror("unimplemented write %02X %02X\n", reg, data);
m_channels[ch].pan_delay = data; m_channels[ch].pan_delay = data;
break; break;
case 0x38: // Pan T/B
m_channels[ch].pan1 = data;
break;
case 0x40: // Pan T/B delayed transition
if (data) logerror("unimplemented write %02X %02X\n", reg, data);
m_channels[ch].pan1_delay = data;
break;
case 0x48: // Loop case 0x48: // Loop
m_channels[ch].loop = data; m_channels[ch].loop = data;
break; break;
@ -485,7 +503,7 @@ void ymz774_device::internal_reg_write(uint8_t reg, uint8_t data)
} }
else if (reg < 0xd0) else if (reg < 0xd0)
{ {
if (m_bank == 0) // Sequencer, is it used in PGM2 ? if (m_bank == 0) // Sequencer, in PGM2 games not used at all
{ {
if (data) logerror("sequencer unimplemented %02X %02X\n", reg, data); if (data) logerror("sequencer unimplemented %02X %02X\n", reg, data);
} }
@ -498,6 +516,9 @@ void ymz774_device::internal_reg_write(uint8_t reg, uint8_t data)
case 0xd0: case 0xd0:
m_vlma = data; m_vlma = data;
break; break;
case 0xd1:
m_vlma1 = data;
break;
case 0xd2: case 0xd2:
m_cpl = data; m_cpl = data;
break; break;

View File

@ -11,10 +11,6 @@
#pragma once #pragma once
//**************************************************************************
// CONSTANTS
//**************************************************************************
//************************************************************************** //**************************************************************************
// INTERFACE CONFIGURATION MACROS // INTERFACE CONFIGURATION MACROS
//************************************************************************** //**************************************************************************
@ -69,7 +65,8 @@ protected:
uint8_t m_cur_reg; uint8_t m_cur_reg;
uint8_t m_mute; // mute chip uint8_t m_mute; // mute chip
uint8_t m_doen; // digital output enable uint8_t m_doen; // digital output enable
uint8_t m_vlma; // overall AAM volume uint8_t m_vlma; // overall volume L0/R0
uint8_t m_vlma1; // overall volume L1/R1
uint8_t m_bsl; // boost level uint8_t m_bsl; // boost level
uint8_t m_cpl; // clip limiter uint8_t m_cpl; // clip limiter
required_region_ptr<uint8_t> m_rom; required_region_ptr<uint8_t> m_rom;
@ -79,6 +76,8 @@ protected:
uint16_t phrase; uint16_t phrase;
uint8_t pan; uint8_t pan;
uint8_t pan_delay; uint8_t pan_delay;
uint8_t pan1;
uint8_t pan1_delay;
uint8_t volume; uint8_t volume;
uint8_t volume_delay; uint8_t volume_delay;
uint8_t volume2; uint8_t volume2;
@ -97,10 +96,10 @@ protected:
struct ymz_sequence struct ymz_sequence
{ {
uint16_t sequence; uint16_t sequence;
uint8_t seqcontrol; uint8_t control;
uint8_t seqdelay; uint8_t delay;
uint8_t *seqdata; uint8_t *data;
bool is_seq_playing; bool is_playing;
}; };
ymz_channel m_channels[16]; ymz_channel m_channels[16];
@ -117,7 +116,7 @@ public:
DECLARE_READ8_MEMBER(read); DECLARE_READ8_MEMBER(read);
protected: protected:
virtual void internal_reg_write(uint8_t reg, uint8_t data) override; virtual void internal_reg_write(uint8_t reg, uint8_t data) override;
virtual uint32_t get_phrase_offs(int phrase) override { int ph = phrase * 4; return ((m_rom[ph] & 0x0f) << 24 | m_rom[ph + 1] << 16 | m_rom[ph + 2] << 8 | m_rom[ph + 3]) * 2; }; virtual uint32_t get_phrase_offs(int phrase) override { int ph = phrase * 4; return ((m_rom[ph] & 0x0f) << 24 | m_rom[ph + 1] << 16 | m_rom[ph + 2] << 8 | m_rom[ph + 3]) * 2; };
virtual uint32_t get_seq_offs(int sqn) override { int sq = sqn * 4 + 0x2000; return ((m_rom[sq] & 0x0f) << 24 | m_rom[sq + 1] << 16 | m_rom[sq + 2] << 8 | m_rom[sq + 3]) * 2; }; virtual uint32_t get_seq_offs(int sqn) override { int sq = sqn * 4 + 0x2000; return ((m_rom[sq] & 0x0f) << 24 | m_rom[sq + 1] << 16 | m_rom[sq + 2] << 8 | m_rom[sq + 3]) * 2; };
virtual void sequencer() override {}; virtual void sequencer() override {};

View File

@ -471,7 +471,7 @@ void pgm2_state::draw_sprites(screen_device &screen, const rectangle &cliprect,
if (spriteram[i] == 0x80000000) if (spriteram[i] == 0x80000000)
{ {
endoflist = i; endoflist = i;
i = 0x2000; break;
} }
} }
@ -1180,7 +1180,7 @@ DRIVER_INIT_MEMBER(pgm2_state,orleg2)
if (rom[0x12620 / 2] == 0xd301) // 104 / 103 if (rom[0x12620 / 2] == 0xd301) // 104 / 103
{ {
hackaddress = 0x12620; hackaddress = 0x12620; // RAM: 10012620
} }
else if (rom[0x1257C / 2] == 0xd301) // 101 else if (rom[0x1257C / 2] == 0xd301) // 101
{ {
@ -1189,8 +1189,8 @@ DRIVER_INIT_MEMBER(pgm2_state,orleg2)
if (hackaddress != -1) if (hackaddress != -1)
{ {
rom[(hackaddress + 2) / 2] = 0x0009; rom[(hackaddress + 2) / 2] = 0x2300;
rom[(hackaddress + 4) / 2] = 0x0009; rom[(hackaddress + 4) / 2] = 0x2300;
rom = (uint16_t*)memregion("maincpu")->base(); // BEQ -> BNE for checksum rom = (uint16_t*)memregion("maincpu")->base(); // BEQ -> BNE for checksum
rom[0x39f2 / 2] = 0x1a00; rom[0x39f2 / 2] = 0x1a00;

View File

@ -6,6 +6,8 @@ QT Computer Systems SBC +2/4
2009-12-11 Skeleton driver. 2009-12-11 Skeleton driver.
This looks like the same system as the Compu/Time SBC-880 Processor Board.
Currently it crashes. There's a memory move routine at 50A4, and after Currently it crashes. There's a memory move routine at 50A4, and after
a few turns it is told to move E603 bytes which corrupts everything. a few turns it is told to move E603 bytes which corrupts everything.
@ -15,6 +17,20 @@ Chips: P8251, D8253C, MK3880N-4 (Z80). 3x 6-sw dips. Unmarked crystal.
There's a blue jumper marked 4M and 2M. Assumed to be selectable CPU clock. There's a blue jumper marked 4M and 2M. Assumed to be selectable CPU clock.
Also assumed this is what the "2/4" in the name refers to. Also assumed this is what the "2/4" in the name refers to.
Feature list from QT ad:
- 1K RAM (which can be located at any 1K boundary) plus one each
Parallel and Serial I/O ports on board
- Power on jump to onboard EPROM (2708 or 2716)
- EPROM addressable on any 1K or 2K boundary
- Full 64K use of RAM allowed in shadow mode
- Programmable Baud rate selection, 110-9600
- 2 or 4MHz switch selectable
- DMA capability allows MWRT signal generation on CPU board or elsewhere
in system under DMA logic or front panel control
- Two programmable timers available for use by programs run with the
SBC+2/4 (timer output and controls available at parallel I/O connector;
parallel input and output ports available for use on CPU board).
****************************************************************************/ ****************************************************************************/
#include "emu.h" #include "emu.h"
@ -22,6 +38,7 @@ Also assumed this is what the "2/4" in the name refers to.
#include "machine/i8251.h" #include "machine/i8251.h"
#include "machine/pit8253.h" #include "machine/pit8253.h"
#include "bus/rs232/rs232.h" #include "bus/rs232/rs232.h"
//#include "bus/s100/s100.h"
class qtsbc_state : public driver_device class qtsbc_state : public driver_device