diff options
Diffstat (limited to 'src/devices/sound/pokey.cpp')
-rw-r--r-- | src/devices/sound/pokey.cpp | 655 |
1 files changed, 336 insertions, 319 deletions
diff --git a/src/devices/sound/pokey.cpp b/src/devices/sound/pokey.cpp index 29baecb352e..8b1125495b5 100644 --- a/src/devices/sound/pokey.cpp +++ b/src/devices/sound/pokey.cpp @@ -2,15 +2,34 @@ // copyright-holders:Brad Oliver, Eric Smith, Juergen Buchmueller /***************************************************************************** * - * POKEY chip emulator 4.6 + * POKEY chip emulator 4.9 * * Based on original info found in Ron Fries' Pokey emulator, * with additions by Brad Oliver, Eric Smith and Juergen Buchmueller, * paddle (a/d conversion) details from the Atari 400/800 Hardware Manual. * Polynomial algorithms according to info supplied by Perry McFarlane. + * Additional improvements from Mike Saarna's A7800 MAME fork. + * + * 4.9: + * - Two-tone mode updated for better accuracy. + * + * 4.8: + * - Poly5 related modes had a pitch shift issue. The poly4/5 init routine + * was replaced with one based on Altira's implementation, which resolved + * the issue. + * + * 4.7: + * [1] https://www.virtualdub.org/downloads/Altirra%20Hardware%20Reference%20Manual.pdf + * - updated to reflect that borrowing cycle delays only impacts voices + * running at 1.79MHz. (+4 cycles unlinked, or +7 cycles linked) + * At slower speeds, cycle overhead still occurs, but only affects + * the phase of the timer period, not the actual length. + * - Initial two-tone support added. Emulation of two-tone is limited to + * audio output effects, and doesn't incorporate any of the aspects of + * SIO serial transfer. * * 4.6: - * [1] http://ploguechipsounds.blogspot.de/2009/10/how-i-recorded-and-decoded-pokeys.html + * [2] http://ploguechipsounds.blogspot.de/2009/10/how-i-recorded-and-decoded-pokeys.html * - changed audio emulation to emulate borrow 3 clock delay and * proper channel reset. New frequency only becomes effective * after the counter hits 0. Emulation also treats counters @@ -19,6 +38,7 @@ * * 4.51: * - changed to use the attotime datatype + * * 4.5: * - changed the 9/17 bit polynomial formulas such that the values * required for the Tempest Pokey protection will be found. @@ -29,22 +49,27 @@ * - reading the RNG returns the shift register contents ^ 0xff. * That way resetting the Pokey with SKCTL (which resets the * polynomial shifters to 0) returns the expected 0xff value. + * * 4.4: * - reversed sample values to make OFF channels produce a zero signal. * actually de-reversed them; don't remember that I reversed them ;-/ + * * 4.3: * - for POT inputs returning zero, immediately assert the ALLPOT * bit after POTGO is written, otherwise start trigger timer * depending on SK_PADDLE mode, either 1-228 scanlines or 1-2 * scanlines, depending on the SK_PADDLE bit of SKCTL. + * * 4.2: * - half volume for channels which are inaudible (this should be * close to the real thing). + * * 4.1: * - default gain increased to closely match the old code. * - random numbers repeat rate depends on POLY9 flag too! * - verified sound output with many, many Atari 800 games, * including the SUPPRESS_INAUDIBLE optimizations. + * * 4.0: * - rewritten from scratch. * - 16bit stream interface. @@ -75,21 +100,24 @@ #define POKEY_DEFAULT_GAIN (32767/11/4) -#define VERBOSE 0 -#define VERBOSE_SOUND 0 -#define VERBOSE_TIMER 0 -#define VERBOSE_POLY 0 -#define VERBOSE_RAND 0 +#define VERBOSE_SOUND (1U << 1) +#define VERBOSE_TIMER (1U << 2) +#define VERBOSE_POLY (1U << 3) +#define VERBOSE_RAND (1U << 4) +#define VERBOSE_IRQ (1U << 5) +#define VERBOSE (0) + +#include "logmacro.h" -#define LOG(x) do { if (VERBOSE) logerror x; } while (0) +#define LOG_SOUND(...) LOGMASKED(VERBOSE_SOUND, __VA_ARGS__) -#define LOG_SOUND(x) do { if (VERBOSE_SOUND) logerror x; } while (0) +#define LOG_TIMER(...) LOGMASKED(VERBOSE_TIMER, __VA_ARGS__) -#define LOG_TIMER(x) do { if (VERBOSE_TIMER) logerror x; } while (0) +#define LOG_POLY(...) LOGMASKED(VERBOSE_POLY, __VA_ARGS__) -#define LOG_POLY(x) do { if (VERBOSE_POLY) logerror x; } while (0) +#define LOG_RAND(...) LOGMASKED(VERBOSE_RAND, __VA_ARGS__) -#define LOG_RAND(x) do { if (VERBOSE_RAND) logerror x; } while (0) +#define LOG_IRQ(...) LOGMASKED(VERBOSE_IRQ, __VA_ARGS__) #define CHAN1 0 #define CHAN2 1 @@ -139,7 +167,7 @@ /* SKCTL (W/D20F) */ #define SK_BREAK 0x80 /* serial out break signal */ #define SK_BPS 0x70 /* bits per second */ -#define SK_FM 0x08 /* FM mode */ +#define SK_TWOTONE 0x08 /* Two tone mode */ #define SK_PADDLE 0x04 /* fast paddle a/d conversion */ #define SK_RESET 0x03 /* reset serial/keyboard interface */ #define SK_KEYSCAN 0x02 /* key scanning enabled ? */ @@ -152,9 +180,6 @@ #define CLK_28 1 #define CLK_114 2 -constexpr unsigned pokey_device::FREQ_17_EXACT; - - // device type definition DEFINE_DEVICE_TYPE(POKEY, pokey_device, "pokey", "Atari C012294 POKEY") @@ -167,18 +192,23 @@ DEFINE_DEVICE_TYPE(POKEY, pokey_device, "pokey", "Atari C012294 POKEY") // pokey_device - constructor //------------------------------------------------- -pokey_device::pokey_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, POKEY, tag, owner, clock), - device_sound_interface(mconfig, *this), - device_execute_interface(mconfig, *this), - device_state_interface(mconfig, *this), - m_icount(0), - m_stream(nullptr), - m_pot_r_cb{ {*this}, {*this}, {*this}, {*this}, {*this}, {*this}, {*this}, {*this} }, - m_allpot_r_cb(*this), - m_serin_r_cb(*this), - m_serout_w_cb(*this), - m_output_type(LEGACY_LINEAR) +pokey_device::pokey_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, POKEY, tag, owner, clock), + device_sound_interface(mconfig, *this), + device_execute_interface(mconfig, *this), + device_state_interface(mconfig, *this), + m_icount(0), + m_stream(nullptr), + m_pot_r_cb(*this, 0), + m_allpot_r_cb(*this, 0), + m_serin_r_cb(*this, 0), + m_serout_w_cb(*this), + m_irq_w_cb(*this), + m_keyboard_r(*this), + m_output_type(LEGACY_LINEAR), + m_serout_ready_timer(nullptr), + m_serout_complete_timer(nullptr), + m_serin_ready_timer(nullptr) { } @@ -189,21 +219,16 @@ pokey_device::pokey_device(const machine_config &mconfig, const char *tag, devic void pokey_device::device_start() { //int sample_rate = clock(); - int i; - /* Setup channels */ - for (i=0; i<POKEY_CHANNELS; i++) - { - m_channel[i].m_parent = this; - m_channel[i].m_INTMask = 0; - } + // Set up channels + for (pokey_channel &chan : m_channel) + chan.m_INTMask = 0; m_channel[CHAN1].m_INTMask = IRQ_TIMR1; m_channel[CHAN2].m_INTMask = IRQ_TIMR2; m_channel[CHAN4].m_INTMask = IRQ_TIMR4; - // bind callbacks - m_keyboard_r.bind_relative_to(*owner()); - m_irq_f.bind_relative_to(*owner()); + // bind delegates + m_keyboard_r.resolve(); /* calculate the A/D times * In normal, slow mode (SKCTL bit SK_PADDLE is clear) the conversion @@ -214,20 +239,29 @@ void pokey_device::device_start() */ /* initialize the poly counters */ - poly_init_4_5(m_poly4, 4, 1, 0); - poly_init_4_5(m_poly5, 5, 2, 1); + poly_init_4_5(m_poly4, 4); + poly_init_4_5(m_poly5, 5); /* initialize 9 / 17 arrays */ poly_init_9_17(m_poly9, 9); poly_init_9_17(m_poly17, 17); vol_init(); + for (int i=0; i<4; i++) + m_channel[i].m_AUDC = 0xb0; + /* The pokey does not have a reset line. These should be initialized * with random values. */ - m_KBCODE = 0x09; /* Atari 800 'no key' */ - m_SKCTL = SK_RESET; /* let the RNG run after reset */ + m_KBCODE = 0x09; // Atari 800 'no key' + m_SKCTL = 0; + + // TODO: several a7800 demos don't explicitly reset pokey at startup + // See https://atariage.com/forums/topic/337317-a7800-52-release/ and + // https://atariage.com/forums/topic/268458-a7800-the-atari-7800-emulator/?do=findComment&comment=5079170) + // m_SKCTL = SK_RESET; + m_SKSTAT = 0; /* This bit should probably get set later. Acid5200 pokey_setoc test tests this. */ m_IRQST = IRQ_SEROC; @@ -247,38 +281,21 @@ void pokey_device::device_start() m_kbd_state = 0; /* reset more internal state */ - for (i=0; i<3; i++) - { - m_clock_cnt[i] = 0; - } - - for (i=0; i<8; i++) - { - m_POTx[i] = 0; - } - - for (devcb_read8 &cb : m_pot_r_cb) - cb.resolve(); - m_allpot_r_cb.resolve(); - m_serin_r_cb.resolve(); - m_serout_w_cb.resolve_safe(); + std::fill(std::begin(m_clock_cnt), std::end(m_clock_cnt), 0); + std::fill(std::begin(m_POTx), std::end(m_POTx), 0); m_stream = stream_alloc(0, 1, clock()); - timer_alloc(SYNC_WRITE); /* timer for sync operation */ - timer_alloc(SYNC_NOOP); - timer_alloc(SYNC_POT); - timer_alloc(SYNC_SET_IRQST); + m_serout_ready_timer = timer_alloc(FUNC(pokey_device::serout_ready_irq), this); + m_serout_complete_timer = timer_alloc(FUNC(pokey_device::serout_complete_irq), this); + m_serin_ready_timer = timer_alloc(FUNC(pokey_device::serin_ready_irq), this); - for (i=0; i<POKEY_CHANNELS; i++) - { - save_item(NAME(m_channel[i].m_borrow_cnt), i); - save_item(NAME(m_channel[i].m_counter), i); - save_item(NAME(m_channel[i].m_filter_sample), i); - save_item(NAME(m_channel[i].m_output), i); - save_item(NAME(m_channel[i].m_AUDF), i); - save_item(NAME(m_channel[i].m_AUDC), i); - } + save_item(STRUCT_MEMBER(m_channel, m_borrow_cnt)); + save_item(STRUCT_MEMBER(m_channel, m_counter)); + save_item(STRUCT_MEMBER(m_channel, m_filter_sample)); + save_item(STRUCT_MEMBER(m_channel, m_output)); + save_item(STRUCT_MEMBER(m_channel, m_AUDF)); + save_item(STRUCT_MEMBER(m_channel, m_AUDC)); save_item(NAME(m_clock_cnt)); save_item(NAME(m_p4)); @@ -333,6 +350,10 @@ void pokey_device::device_start() void pokey_device::device_reset() { m_stream->update(); + // a1200xl reads POT4 twice at startup for reading self-test mode jumpers. + // we need to update POT counters here otherwise it will boot to self-test + // the first time around no matter the setting. + pokey_potgo(); } @@ -364,60 +385,59 @@ void pokey_device::device_clock_changed() } //------------------------------------------------- -// stream_generate - handle update requests for -// our sound stream +// timer callbacks //------------------------------------------------- -void pokey_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +TIMER_CALLBACK_MEMBER(pokey_device::serout_ready_irq) { - switch (id) + if (m_IRQEN & IRQ_SEROR) { - case 3: - /* serout_ready_cb */ - if (m_IRQEN & IRQ_SEROR) - { - m_IRQST |= IRQ_SEROR; - if (!m_irq_f.isnull()) - m_irq_f(IRQ_SEROR); - } - break; - case 4: - /* serout_complete */ - if (m_IRQEN & IRQ_SEROC) - { - m_IRQST |= IRQ_SEROC; - if (!m_irq_f.isnull()) - m_irq_f(IRQ_SEROC); - } - break; - case 5: - /* serin_ready */ - if (m_IRQEN & IRQ_SERIN) - { - m_IRQST |= IRQ_SERIN; - if (!m_irq_f.isnull()) - m_irq_f(IRQ_SERIN); - } - break; - case SYNC_WRITE: - { - offs_t offset = (param >> 8) & 0xff; - uint8_t data = param & 0xff; - write_internal(offset, data); - } - break; - case SYNC_NOOP: - /* do nothing, caused by a forced resync */ - break; - case SYNC_POT: - //logerror("x %02x \n", (param & 0x20)); - m_ALLPOT |= (param & 0xff); - break; - case SYNC_SET_IRQST: + m_IRQST |= IRQ_SEROR; + LOG_IRQ("POKEY SEROR IRQ raised\n"); + m_irq_w_cb(ASSERT_LINE); + } +} + +TIMER_CALLBACK_MEMBER(pokey_device::serout_complete_irq) +{ + m_IRQST |= IRQ_SEROC; + if (m_IRQEN & IRQ_SEROC) + { + LOG_IRQ("POKEY SEROC IRQ raised\n"); + m_irq_w_cb(ASSERT_LINE); + } +} + +TIMER_CALLBACK_MEMBER(pokey_device::serin_ready_irq) +{ + if (m_IRQEN & IRQ_SERIN) + { + m_IRQST |= IRQ_SERIN; + LOG_IRQ("POKEY SERIN IRQ raised\n"); + m_irq_w_cb(ASSERT_LINE); + } +} + +TIMER_CALLBACK_MEMBER(pokey_device::sync_write) +{ + offs_t offset = (param >> 8) & 0xff; + uint8_t data = param & 0xff; + write_internal(offset, data); +} + +TIMER_CALLBACK_MEMBER(pokey_device::sync_pot) +{ + //logerror("x %02x \n", (param & 0x20)); + m_ALLPOT |= (param & 0xff); +} + +TIMER_CALLBACK_MEMBER(pokey_device::sync_set_irqst) +{ + if (m_IRQEN & param) + { + LOG_IRQ("POKEY TIMR%d IRQ raised\n", param); m_IRQST |= (param & 0xff); - break; - default: - throw emu_fatalerror("Unknown id in pokey_device::device_timer"); + m_irq_w_cb(ASSERT_LINE); } } @@ -453,9 +473,9 @@ void pokey_device::step_keyboard() /* check if the break IRQ is enabled */ if (m_IRQEN & IRQ_BREAK) { + LOG_IRQ("POKEY BREAK IRQ raised\n"); m_IRQST |= IRQ_BREAK; - if (!m_irq_f.isnull()) - m_irq_f(IRQ_BREAK); + m_irq_w_cb(ASSERT_LINE); } } break; @@ -481,20 +501,20 @@ void pokey_device::step_keyboard() } break; case 1: /* waiting for key confirmation */ - if ((m_kbd_latch & 0x3f) == m_kbd_cnt) + if (!(m_SKCTL & SK_DEBOUNCE) || (m_kbd_latch & 0x3f) == m_kbd_cnt) { if (ret & 1) { - m_KBCODE = m_kbd_latch; + m_KBCODE = (m_SKCTL & SK_DEBOUNCE) ? m_kbd_latch : (m_kbd_latch & 0xc0) | m_kbd_cnt; m_SKSTAT |= SK_KEYBD; if (m_IRQEN & IRQ_KEYBD) { /* last interrupt not acknowledged ? */ if (m_IRQST & IRQ_KEYBD) m_SKSTAT |= SK_KBERR; + LOG_IRQ("POKEY KEYBD IRQ raised\n"); m_IRQST |= IRQ_KEYBD; - if (!m_irq_f.isnull()) - m_irq_f(IRQ_KEYBD); + m_irq_w_cb(ASSERT_LINE); } m_kbd_state++; } @@ -503,16 +523,14 @@ void pokey_device::step_keyboard() } break; case 2: /* waiting for release */ - if ((m_kbd_latch & 0x3f) == m_kbd_cnt) + if (!(m_SKCTL & SK_DEBOUNCE) || (m_kbd_latch & 0x3f) == m_kbd_cnt) { if ((ret & 1)==0) m_kbd_state++; - else - m_SKSTAT |= SK_KEYBD; } break; case 3: - if ((m_kbd_latch & 0x3f) == m_kbd_cnt) + if (!(m_SKCTL & SK_DEBOUNCE) || (m_kbd_latch & 0x3f) == m_kbd_cnt) { if (ret & 1) m_kbd_state = 2; @@ -541,7 +559,7 @@ void pokey_device::step_pot() } // some pots latched? if (upd != 0) - synchronize(SYNC_POT, upd); + machine().scheduler().synchronize(timer_expired_delegate(FUNC(pokey_device::sync_pot), this), upd); } /* @@ -552,11 +570,12 @@ void pokey_device::step_pot() * */ -void pokey_device::step_one_clock(void) +void pokey_device::step_one_clock() { - /* Clocks only count if we are not in a reset */ if (m_SKCTL & SK_RESET) { + /* Clocks only count if we are not in a reset */ + /* polynom pointers */ if (++m_p4 == 0x0000f) m_p4 = 0; @@ -582,21 +601,36 @@ void pokey_device::step_one_clock(void) clock_triggered[CLK_114] = 1; } - int const base_clock = (m_AUDCTL & CLK_15KHZ) ? CLK_114 : CLK_28; - int clk = (m_AUDCTL & CH1_HICLK) ? CLK_1 : base_clock; - if (clock_triggered[clk]) - m_channel[CHAN1].inc_chan(); + if ((m_AUDCTL & CH1_HICLK) && (clock_triggered[CLK_1])) + { + if (m_AUDCTL & CH12_JOINED) + m_channel[CHAN1].inc_chan(*this, 7); + else + m_channel[CHAN1].inc_chan(*this, 4); + } + + int base_clock = (m_AUDCTL & CLK_15KHZ) ? CLK_114 : CLK_28; - clk = (m_AUDCTL & CH3_HICLK) ? CLK_1 : base_clock; - if (clock_triggered[clk]) - m_channel[CHAN3].inc_chan(); + if ((!(m_AUDCTL & CH1_HICLK)) && (clock_triggered[base_clock])) + m_channel[CHAN1].inc_chan(*this, 1); + + if ((m_AUDCTL & CH3_HICLK) && (clock_triggered[CLK_1])) + { + if (m_AUDCTL & CH34_JOINED) + m_channel[CHAN3].inc_chan(*this, 7); + else + m_channel[CHAN3].inc_chan(*this, 4); + } + + if ((!(m_AUDCTL & CH3_HICLK)) && (clock_triggered[base_clock])) + m_channel[CHAN3].inc_chan(*this, 1); if (clock_triggered[base_clock]) { if (!(m_AUDCTL & CH12_JOINED)) - m_channel[CHAN2].inc_chan(); + m_channel[CHAN2].inc_chan(*this, 1); if (!(m_AUDCTL & CH34_JOINED)) - m_channel[CHAN4].inc_chan(); + m_channel[CHAN4].inc_chan(*this, 1); } /* Potentiometer handling */ @@ -608,38 +642,26 @@ void pokey_device::step_one_clock(void) step_keyboard(); } - /* do CHAN2 before CHAN1 because CHAN1 may set borrow! */ - if (m_channel[CHAN2].check_borrow()) + if (m_channel[CHAN3].check_borrow()) { - bool const isJoined(m_AUDCTL & CH12_JOINED); - if (isJoined) - m_channel[CHAN1].reset_channel(); - m_channel[CHAN2].reset_channel(); - process_channel(CHAN2); - - /* check if some of the requested timer interrupts are enabled */ - if ((m_IRQST & IRQ_TIMR2) && !m_irq_f.isnull()) - m_irq_f(IRQ_TIMR2); - } + if (m_AUDCTL & CH34_JOINED) + m_channel[CHAN4].inc_chan(*this, 1); + else + m_channel[CHAN3].reset_channel(); - if (m_channel[CHAN1].check_borrow()) - { - bool const isJoined(m_AUDCTL & CH12_JOINED); - if (isJoined) - m_channel[CHAN2].inc_chan(); + process_channel(CHAN3); + /* is this a filtering channel (3/4) and is the filter active? */ + if (m_AUDCTL & CH1_FILTER) + m_channel[CHAN1].sample(); else - m_channel[CHAN1].reset_channel(); - process_channel(CHAN1); - /* check if some of the requested timer interrupts are enabled */ - if ((m_IRQST & IRQ_TIMR1) && !m_irq_f.isnull()) - m_irq_f(IRQ_TIMR1); + m_channel[CHAN1].m_filter_sample = 1; + + m_old_raw_inval = true; } - /* do CHAN4 before CHAN3 because CHAN3 may set borrow! */ if (m_channel[CHAN4].check_borrow()) { - bool const isJoined(m_AUDCTL & CH34_JOINED); - if (isJoined) + if (m_AUDCTL & CH34_JOINED) m_channel[CHAN3].reset_channel(); m_channel[CHAN4].reset_channel(); process_channel(CHAN4); @@ -648,23 +670,36 @@ void pokey_device::step_one_clock(void) m_channel[CHAN2].sample(); else m_channel[CHAN2].m_filter_sample = 1; - if ((m_IRQST & IRQ_TIMR4) && !m_irq_f.isnull()) - m_irq_f(IRQ_TIMR4); + + m_old_raw_inval = true; } - if (m_channel[CHAN3].check_borrow()) + if ((m_SKCTL & SK_TWOTONE) && (m_channel[CHAN2].m_borrow_cnt == 1)) { - bool const isJoined(m_AUDCTL & CH34_JOINED); - if (isJoined) - m_channel[CHAN4].inc_chan(); - else - m_channel[CHAN3].reset_channel(); - process_channel(CHAN3); - /* is this a filtering channel (3/4) and is the filter active? */ - if (m_AUDCTL & CH1_FILTER) - m_channel[CHAN1].sample(); + m_channel[CHAN1].reset_channel(); + m_old_raw_inval = true; + } + + if (m_channel[CHAN1].check_borrow()) + { + if (m_AUDCTL & CH12_JOINED) + m_channel[CHAN2].inc_chan(*this, 1); else - m_channel[CHAN1].m_filter_sample = 1; + m_channel[CHAN1].reset_channel(); + + // TODO: If two-tone is enabled *and* serial output == 1 then reset the channel 2 timer. + + process_channel(CHAN1); + } + + if (m_channel[CHAN2].check_borrow()) + { + if (m_AUDCTL & CH12_JOINED) + m_channel[CHAN1].reset_channel(); + + m_channel[CHAN2].reset_channel(); + + process_channel(CHAN2); } if (m_old_raw_inval) @@ -677,25 +712,20 @@ void pokey_device::step_one_clock(void) } if (m_out_raw != sum) - { - //printf("forced update %08d %08x\n", m_icount, m_out_raw); m_stream->update(); - } + m_old_raw_inval = false; m_out_raw = sum; } } //------------------------------------------------- -// stream_generate - handle update requests for +// sound_stream_update - handle update requests for // our sound stream //------------------------------------------------- - -void pokey_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +void pokey_device::sound_stream_update(sound_stream &stream) { - stream_sample_t *buffer = outputs[0]; - if (m_output_type == LEGACY_LINEAR) { int32_t out = 0; @@ -703,26 +733,21 @@ void pokey_device::sound_stream_update(sound_stream &stream, stream_sample_t **i out += ((m_out_raw >> (4*i)) & 0x0f); out *= POKEY_DEFAULT_GAIN; out = (out > 0x7fff) ? 0x7fff : out; - while( samples > 0 ) - { - *buffer++ = out; - samples--; - } + sound_stream::sample_t outsamp = out * sound_stream::sample_t(1.0 / 32768.0); + stream.fill(0, outsamp); } else if (m_output_type == RC_LOWPASS) { double rTot = m_voltab[m_out_raw]; - double V0 = rTot / (rTot+m_r_pullup) * m_v_ref / 5.0 * 32767.0; + double V0 = rTot / (rTot+m_r_pullup) * m_v_ref / 5.0; double mult = (m_cap == 0.0) ? 1.0 : 1.0 - exp(-(rTot + m_r_pullup) / (m_cap * m_r_pullup * rTot) * m_clock_period.as_double()); - while( samples > 0 ) + for (int sampindex = 0; sampindex < stream.samples(); sampindex++) { /* store sum of output signals into the buffer */ m_out_filter += (V0 - m_out_filter) * mult; - *buffer++ = m_out_filter; - samples--; - + stream.put(0, sampindex, m_out_filter); } } else if (m_output_type == OPAMP_C_TO_GROUND) @@ -737,15 +762,8 @@ void pokey_device::sound_stream_update(sound_stream &stream, stream_sample_t **i * It is approximated by eliminating m_v_ref ( -1.0 term) */ - double V0 = ((rTot+m_r_pullup) / rTot - 1.0) * m_v_ref / 5.0 * 32767.0; - - while( samples > 0 ) - { - /* store sum of output signals into the buffer */ - *buffer++ = V0; - samples--; - - } + double V0 = ((rTot+m_r_pullup) / rTot - 1.0) * m_v_ref / 5.0; + stream.fill(0, V0); } else if (m_output_type == OPAMP_LOW_PASS) { @@ -754,25 +772,19 @@ void pokey_device::sound_stream_update(sound_stream &stream, stream_sample_t **i * It is approximated by not adding in VRef below. */ - double V0 = (m_r_pullup / rTot) * m_v_ref / 5.0 * 32767.0; + double V0 = (m_r_pullup / rTot) * m_v_ref / 5.0; double mult = (m_cap == 0.0) ? 1.0 : 1.0 - exp(-1.0 / (m_cap * m_r_pullup) * m_clock_period.as_double()); - while( samples > 0 ) + for (int sampindex = 0; sampindex < stream.samples(); sampindex++) { /* store sum of output signals into the buffer */ m_out_filter += (V0 - m_out_filter) * mult; - *buffer++ = m_out_filter /* + m_v_ref */; // see above - samples--; + stream.put(0, sampindex, m_out_filter); } } else if (m_output_type == DISCRETE_VAR_R) { - int32_t out = m_voltab[m_out_raw]; - while( samples > 0 ) - { - *buffer++ = out; - samples--; - } + stream.fill(0, m_voltab[m_out_raw]); } } @@ -784,7 +796,7 @@ uint8_t pokey_device::read(offs_t offset) { int data, pot; - synchronize(SYNC_NOOP); /* force resync */ + machine().scheduler().synchronize(); /* force resync */ switch (offset & 15) { @@ -795,12 +807,12 @@ uint8_t pokey_device::read(offs_t offset) { /* we have a value measured */ data = m_POTx[pot]; - LOG(("POKEY '%s' read POT%d (final value) $%02x\n", tag(), pot, data)); + LOG("%s: POKEY read POT%d (final value) $%02x\n", machine().describe_context(), pot, data); } else { data = m_pot_counter; - LOG(("POKEY '%s' read POT%d (interpolated) $%02x\n", tag(), pot, data)); + LOG("%s: POKEY read POT%d (interpolated) $%02x\n", machine().describe_context(), pot, data); } break; @@ -812,17 +824,17 @@ uint8_t pokey_device::read(offs_t offset) if ((m_SKCTL & SK_RESET) == 0) { data = m_ALLPOT; - LOG(("POKEY '%s' ALLPOT internal $%02x (reset)\n", tag(), data)); + LOG("%s: POKEY ALLPOT internal $%02x (reset)\n", machine().describe_context(), data); } - else if (!m_allpot_r_cb.isnull()) + else if (!m_allpot_r_cb.isunset()) { m_ALLPOT = data = m_allpot_r_cb(offset); - LOG(("%s: POKEY '%s' ALLPOT callback $%02x\n", machine().describe_context(), tag(), data)); + LOG("%s: POKEY ALLPOT callback $%02x\n", machine().describe_context(), data); } else { data = m_ALLPOT ^ 0xff; - LOG(("POKEY '%s' ALLPOT internal $%02x\n", tag(), data)); + LOG("%s: POKEY ALLPOT internal $%02x\n", machine().describe_context(), data); } break; @@ -834,37 +846,37 @@ uint8_t pokey_device::read(offs_t offset) if (m_AUDCTL & POLY9) { data = m_poly9[m_p9] & 0xff; - LOG_RAND(("POKEY '%s' rand9[$%05x]: $%02x\n", tag(), m_p9, data)); + LOG_RAND("%s: POKEY rand9[$%05x]: $%02x\n", machine().describe_context(), m_p9, data); } else { data = (m_poly17[m_p17] >> 8) & 0xff; - LOG_RAND(("POKEY '%s' rand17[$%05x]: $%02x\n", tag(), m_p17, data)); + LOG_RAND("%s: POKEY rand17[$%05x]: $%02x\n", machine().describe_context(), m_p17, data); } break; case SERIN_C: - if (!m_serin_r_cb.isnull()) + if (!m_serin_r_cb.isunset()) m_SERIN = m_serin_r_cb(offset); data = m_SERIN; - LOG(("POKEY '%s' SERIN $%02x\n", tag(), data)); + LOG("%s: POKEY SERIN $%02x\n", machine().describe_context(), data); break; case IRQST_C: /* IRQST is an active low input port; we keep it active high */ /* internally to ease the (un-)masking of bits */ data = m_IRQST ^ 0xff; - LOG(("POKEY '%s' IRQST $%02x\n", tag(), data)); + LOG("%s: POKEY IRQST $%02x\n", machine().describe_context(), data); break; case SKSTAT_C: /* SKSTAT is also an active low input port */ data = m_SKSTAT ^ 0xff; - LOG(("POKEY '%s' SKSTAT $%02x\n", tag(), data)); + LOG("%s: POKEY SKSTAT $%02x\n", machine().describe_context(), data); break; default: - LOG(("POKEY '%s' register $%02x\n", tag(), offset)); + LOG("%s: POKEY register $%02x\n", machine().describe_context(), offset); data = 0xff; break; } @@ -878,7 +890,7 @@ uint8_t pokey_device::read(offs_t offset) void pokey_device::write(offs_t offset, uint8_t data) { - synchronize(SYNC_WRITE, (offset << 8) | data); + machine().scheduler().synchronize(timer_expired_delegate(FUNC(pokey_device::sync_write), this), (offset << 8) | data); } void pokey_device::write_internal(offs_t offset, uint8_t data) @@ -887,59 +899,59 @@ void pokey_device::write_internal(offs_t offset, uint8_t data) switch (offset & 15) { case AUDF1_C: - LOG_SOUND(("POKEY '%s' AUDF1 $%02x\n", tag(), data)); + LOG_SOUND("%s: AUDF1 = $%02x\n", machine().describe_context(), data); m_channel[CHAN1].m_AUDF = data; break; case AUDC1_C: - LOG_SOUND(("POKEY '%s' AUDC1 $%02x (%s)\n", tag(), data, audc2str(data))); + LOG_SOUND("%s: POKEY AUDC1 $%02x (%s)\n", machine().describe_context(), data, audc2str(data)); m_channel[CHAN1].m_AUDC = data; m_old_raw_inval = true; break; case AUDF2_C: - LOG_SOUND(("POKEY '%s' AUDF2 $%02x\n", tag(), data)); + LOG_SOUND("%s: POKEY AUDF2 $%02x\n", machine().describe_context(), data); m_channel[CHAN2].m_AUDF = data; break; case AUDC2_C: - LOG_SOUND(("POKEY '%s' AUDC2 $%02x (%s)\n", tag(), data, audc2str(data))); + LOG_SOUND("%s: POKEY AUDC2 $%02x (%s)\n", machine().describe_context(), data, audc2str(data)); m_channel[CHAN2].m_AUDC = data; m_old_raw_inval = true; break; case AUDF3_C: - LOG_SOUND(("POKEY '%s' AUDF3 $%02x\n", tag(), data)); + LOG_SOUND("%s: POKEY AUDF3 $%02x\n", machine().describe_context(), data); m_channel[CHAN3].m_AUDF = data; break; case AUDC3_C: - LOG_SOUND(("POKEY '%s' AUDC3 $%02x (%s)\n", tag(), data, audc2str(data))); + LOG_SOUND("%s: POKEY AUDC3 $%02x (%s)\n", machine().describe_context(), data, audc2str(data)); m_channel[CHAN3].m_AUDC = data; m_old_raw_inval = true; break; case AUDF4_C: - LOG_SOUND(("POKEY '%s' AUDF4 $%02x\n", tag(), data)); + LOG_SOUND("%s: POKEY AUDF4 $%02x\n", machine().describe_context(), data); m_channel[CHAN4].m_AUDF = data; break; case AUDC4_C: - LOG_SOUND(("POKEY '%s' AUDC4 $%02x (%s)\n", tag(), data, audc2str(data))); + LOG_SOUND("%s: POKEY AUDC4 $%02x (%s)\n", machine().describe_context(), data, audc2str(data)); m_channel[CHAN4].m_AUDC = data; m_old_raw_inval = true; break; case AUDCTL_C: - if( data == m_AUDCTL ) + if (data == m_AUDCTL) return; - LOG_SOUND(("POKEY '%s' AUDCTL $%02x (%s)\n", tag(), data, audctl2str(data))); + LOG_SOUND("%s: POKEY AUDCTL $%02x (%s)\n", machine().describe_context(), data, audctl2str(data)); m_AUDCTL = data; - + m_old_raw_inval = true; break; case STIMER_C: - LOG_TIMER(("POKEY '%s' STIMER $%02x\n", tag(), data)); + LOG_TIMER("%s: POKEY STIMER $%02x\n", machine().describe_context(), data); /* From the pokey documentation: * reset all counters to zero (side effect) @@ -957,17 +969,23 @@ void pokey_device::write_internal(offs_t offset, uint8_t data) case SKREST_C: /* reset SKSTAT */ - LOG(("POKEY '%s' SKREST $%02x\n", tag(), data)); + LOG("%s: POKEY SKREST $%02x\n", machine().describe_context(), data); m_SKSTAT &= ~(SK_FRAME|SK_OVERRUN|SK_KBERR); break; case POTGO_C: - LOG(("POKEY '%s' POTGO $%02x\n", tag(), data)); - pokey_potgo(); + LOG("%s: POKEY POTGO $%02x\n", machine().describe_context(), data); + if (m_SKCTL & SK_RESET) + pokey_potgo(); break; case SEROUT_C: - LOG(("POKEY '%s' SEROUT $%02x\n", tag(), data)); + LOG("%s: POKEY SEROUT $%02x\n", machine().describe_context(), data); + // TODO: convert to real serial comms, fix timings + // SEROC (1) serial out in progress (0) serial out complete + // in progress status is necessary for a800 telelnk2 to boot + m_IRQST &= ~IRQ_SEROC; + m_serout_w_cb(offset, data); m_SKSTAT |= SK_SEROUT; /* @@ -975,16 +993,16 @@ void pokey_device::write_internal(offs_t offset, uint8_t data) * loaders from Ballblazer and Escape from Fractalus * The real times are unknown */ - timer_set(attotime::from_usec(200), 3); + m_serout_ready_timer->adjust(attotime::from_usec(200)); /* 10 bits (assumption 1 start, 8 data and 1 stop bit) take how long? */ - timer_set(attotime::from_usec(2000), 4);// FUNC(pokey_serout_complete), 0, p); + m_serout_complete_timer->adjust(attotime::from_usec(2000)); break; case IRQEN_C: - LOG(("POKEY '%s' IRQEN $%02x\n", tag(), data)); + LOG("%s: POKEY IRQEN $%02x\n", machine().describe_context(), data); /* acknowledge one or more IRQST bits ? */ - if( m_IRQST & ~data ) + if (m_IRQST & ~data) { /* reset IRQST bits that are masked now, except the SEROC bit (acid5200 pokey_seroc test) */ m_IRQST &= (IRQ_SEROC | data); @@ -994,17 +1012,22 @@ void pokey_device::write_internal(offs_t offset, uint8_t data) /* if SEROC irq is enabled trigger an irq (acid5200 pokey_seroc test) */ if (m_IRQEN & m_IRQST & IRQ_SEROC) { - if (!m_irq_f.isnull()) - m_irq_f(IRQ_SEROC); + LOG_IRQ("POKEY SEROC IRQ enabled\n"); + m_irq_w_cb(ASSERT_LINE); + } + else if (!(m_IRQEN & m_IRQST)) + { + LOG_IRQ("POKEY IRQs all cleared\n"); + m_irq_w_cb(CLEAR_LINE); } break; case SKCTL_C: - if( data == m_SKCTL ) + if (data == m_SKCTL) return; - LOG(("POKEY '%s' SKCTL $%02x\n", tag(), data)); + LOG("%s: POKEY SKCTL $%02x\n", machine().describe_context(), data); m_SKCTL = data; - if( !(data & SK_RESET) ) + if (!(data & SK_RESET)) { write_internal(IRQEN_C, 0); write_internal(SKREST_C, 0); @@ -1022,9 +1045,15 @@ void pokey_device::write_internal(offs_t offset, uint8_t data) m_clock_cnt[0] = 0; m_clock_cnt[1] = 0; m_clock_cnt[2] = 0; - m_old_raw_inval = true; /* FIXME: Serial port reset ! */ } + if (!(data & SK_KEYSCAN)) + { + m_SKSTAT &= ~SK_KEYBD; + m_kbd_cnt = 0; + m_kbd_state = 0; + } + m_old_raw_inval = true; break; } @@ -1038,7 +1067,7 @@ void pokey_device::write_internal(offs_t offset, uint8_t data) } -WRITE_LINE_MEMBER( pokey_device::sid_w ) +void pokey_device::sid_w(int state) { if (state) { @@ -1052,7 +1081,7 @@ WRITE_LINE_MEMBER( pokey_device::sid_w ) void pokey_device::serin_ready(int after) { - timer_set(m_clock_period * after, 5, 0); + m_serin_ready_timer->adjust(m_clock_period * after, 0); } //------------------------------------------------- @@ -1076,30 +1105,24 @@ inline void pokey_device::process_channel(int ch) } -void pokey_device::pokey_potgo(void) +void pokey_device::pokey_potgo() { - int pot; - - if( (m_SKCTL & SK_RESET) == 0) - return; - - LOG(("POKEY #%p pokey_potgo\n", (void *) this)); + LOG("pokey_potgo\n"); m_ALLPOT = 0x00; m_pot_counter = 0; - for( pot = 0; pot < 8; pot++ ) + for (int pot = 0; pot < 8; pot++) { m_POTx[pot] = 228; - if( !m_pot_r_cb[pot].isnull() ) + if (!m_pot_r_cb[pot].isunset()) { int r = m_pot_r_cb[pot](pot); - LOG(("POKEY %s pot_r(%d) returned $%02x\n", tag(), pot, r)); + LOG("POKEY pot_r(%d) returned $%02x\n", pot, r); if (r >= 228) - { r = 228; - } + if (r == 0) { /* immediately set the ready - bit of m_ALLPOT @@ -1137,12 +1160,12 @@ void pokey_device::vol_init() } r_chan[j] = 1.0 / rTot; } - if (VERBOSE) + if (VERBOSE & LOG_GENERAL) for (int j=0; j<16; j++) { rTot = 1.0 / r_chan[j] + 3.0 / r_chan[0]; rTot = 1.0 / rTot; - LOG(("%s: %3d - %4.3f\n", tag(), j, rTot / (rTot+pull_up)*4.75)); + LOG("%3d - %4.3f\n", j, rTot / (rTot+pull_up)*4.75); } for (int j=0; j<0x10000; j++) { @@ -1157,58 +1180,54 @@ void pokey_device::vol_init() } -void pokey_device::poly_init_4_5(uint32_t *poly, int size, int xorbit, int invert) +void pokey_device::poly_init_4_5(uint32_t *poly, int size) { + LOG_POLY("poly %d\n", size); + int mask = (1 << size) - 1; - int i; uint32_t lfsr = 0; - LOG_POLY(("poly %d\n", size)); - for( i = 0; i < mask; i++ ) + int const xorbit = size - 1; + for (int i = 0; i < mask; i++) { - /* calculate next bit */ - int in = !((lfsr >> 0) & 1) ^ ((lfsr >> xorbit) & 1); - lfsr = lfsr >> 1; - lfsr = (in << (size-1)) | lfsr; - *poly = lfsr ^ invert; - LOG_POLY(("%05x: %02x\n", i, *poly)); + lfsr = (lfsr << 1) | (~((lfsr >> 2) ^ (lfsr >> xorbit)) & 1); + *poly = lfsr & mask; poly++; } } void pokey_device::poly_init_9_17(uint32_t *poly, int size) { - int mask = (1 << size) - 1; - int i; - uint32_t lfsr =mask; + LOG_RAND("rand %d\n", size); - LOG_RAND(("rand %d\n", size)); + const uint32_t mask = util::make_bitmask<uint32_t>(size); + uint32_t lfsr = mask; if (size == 17) { - for( i = 0; i < mask; i++ ) + for (uint32_t i = 0; i < mask; i++) { - /* calculate next bit @ 7 */ - int in8 = ((lfsr >> 8) & 1) ^ ((lfsr >> 13) & 1); - int in = (lfsr & 1); + // calculate next bit @ 7 + const uint32_t in8 = BIT(lfsr, 8) ^ BIT(lfsr, 13); + const uint32_t in = BIT(lfsr, 0); lfsr = lfsr >> 1; lfsr = (lfsr & 0xff7f) | (in8 << 7); lfsr = (in << 16) | lfsr; *poly = lfsr; - LOG_RAND(("%05x: %02x\n", i, *poly)); + LOG_RAND("%05x: %02x\n", i, *poly); poly++; } } - else + else // size == 9 { - for( i = 0; i < mask; i++ ) + for (uint32_t i = 0; i < mask; i++) { - /* calculate next bit */ - int in = ((lfsr >> 0) & 1) ^ ((lfsr >> 5) & 1); + // calculate next bit + const uint32_t in = BIT(lfsr, 0) ^ BIT(lfsr, 5); lfsr = lfsr >> 1; lfsr = (in << 8) | lfsr; *poly = lfsr; - LOG_RAND(("%05x: %02x\n", i, *poly)); + LOG_RAND("%05x: %02x\n", i, *poly); poly++; } } @@ -1218,22 +1237,20 @@ void pokey_device::poly_init_9_17(uint32_t *poly, int size) char *pokey_device::audc2str(int val) { static char buff[80]; - if( val & NOTPOLY5 ) + if (val & NOTPOLY5) { - if( val & PURE ) + if (val & PURE) strcpy(buff,"pure"); - else - if( val & POLY4 ) + else if (val & POLY4) strcpy(buff,"poly4"); else strcpy(buff,"poly9/17"); } else { - if( val & PURE ) + if (val & PURE) strcpy(buff,"poly5"); - else - if( val & POLY4 ) + else if (val & POLY4) strcpy(buff,"poly4+poly5"); else strcpy(buff,"poly9/17+poly5"); @@ -1244,33 +1261,33 @@ char *pokey_device::audc2str(int val) char *pokey_device::audctl2str(int val) { static char buff[80]; - if( val & POLY9 ) + if (val & POLY9) strcpy(buff,"poly9"); else strcpy(buff,"poly17"); - if( val & CH1_HICLK ) + if (val & CH1_HICLK) strcat(buff,"+ch1hi"); - if( val & CH3_HICLK ) + if (val & CH3_HICLK) strcat(buff,"+ch3hi"); - if( val & CH12_JOINED ) + if (val & CH12_JOINED) strcat(buff,"+ch1/2"); - if( val & CH34_JOINED ) + if (val & CH34_JOINED) strcat(buff,"+ch3/4"); - if( val & CH1_FILTER ) + if (val & CH1_FILTER) strcat(buff,"+ch1filter"); - if( val & CH2_FILTER ) + if (val & CH2_FILTER) strcat(buff,"+ch2filter"); - if( val & CLK_15KHZ ) + if (val & CLK_15KHZ) strcat(buff,"+clk15"); return buff; } -pokey_device::pokey_channel::pokey_channel() - : m_AUDF(0), - m_AUDC(0), - m_borrow_cnt(0), - m_counter(0), - m_output(0), - m_filter_sample(0) +pokey_device::pokey_channel::pokey_channel() : + m_AUDF(0), + m_AUDC(0), + m_borrow_cnt(0), + m_counter(0), + m_output(0), + m_filter_sample(0) { } |