diff --git a/src/emu/sound/pokey.c b/src/emu/sound/pokey.c index cbf74beeff6..2c136acec82 100644 --- a/src/emu/sound/pokey.c +++ b/src/emu/sound/pokey.c @@ -86,10 +86,10 @@ #define POKEY_DEFAULT_GAIN (32767/11/4) #define VERBOSE 1 -#define VERBOSE_SOUND 1 -#define VERBOSE_TIMER 1 -#define VERBOSE_POLY 1 -#define VERBOSE_RAND 1 +#define VERBOSE_SOUND 0 +#define VERBOSE_TIMER 0 +#define VERBOSE_POLY 0 +#define VERBOSE_RAND 0 #define LOG(x) do { if (VERBOSE) logerror x; } while (0) @@ -180,7 +180,7 @@ struct _pokey_state UINT32 r17; /* rand17 index */ UINT32 clockmult; /* clock multiplier */ device_t *device; - sound_stream * channel; /* streams channel */ + sound_stream * m_stream; /* streams channel */ emu_timer *timer[3]; /* timers for channel 1,2 and 4 events */ attotime timer_period[3]; /* computed periods for these timers */ int timer_param[3]; /* computed parameters for these timers */ @@ -267,16 +267,6 @@ INLINE void process_channel(pokey_state *chip, int ch) { chip->output[ch] ^= 1; } - /* is this a filtering channel (3/4) and is the filter active? */ - - if( chip->AUDCTL & ((CH1_FILTER|CH2_FILTER) & (0x10 >> ch)) ) - { - if( ch >= 2) - { - chip->filter_sample[ch-2] = chip->output[ch-2]; - } - } - } /* @@ -299,7 +289,7 @@ INLINE int check_borrow(pokey_state *chip, int ch) if (chip->borrow_cnt[ch] > 0) { chip->borrow_cnt[ch]--; - return (chip->borrow_cnt[CHAN1] == 0); + return (chip->borrow_cnt[ch] == 0); } return 0; } @@ -376,6 +366,11 @@ static STREAM_UPDATE( pokey_update ) reset_channel(chip, CHAN3); reset_channel(chip, CHAN4); process_channel(chip, CHAN4); + /* is this a filtering channel (3/4) and is the filter active? */ + if (chip->AUDCTL & CH2_FILTER) + chip->filter_sample[CHAN2] = chip->output[CHAN2]; + else + chip->filter_sample[CHAN2] = 1; } if (check_borrow(chip, CHAN3)) @@ -386,6 +381,11 @@ static STREAM_UPDATE( pokey_update ) else reset_channel(chip, CHAN3); process_channel(chip, CHAN3); + /* is this a filtering channel (3/4) and is the filter active? */ + if (chip->AUDCTL & CH1_FILTER) + chip->filter_sample[CHAN1] = chip->output[CHAN1]; + else + chip->filter_sample[CHAN1] = 1; } for (ch = 0; ch < 4; ch++) @@ -527,7 +527,7 @@ static DEVICE_START( pokey ) chip->serout_w.resolve(chip->intf.serout_w, *device); chip->interrupt_cb = chip->intf.interrupt_cb; - chip->channel = device->machine().sound().stream_alloc(*device, 0, 1, sample_rate, chip, pokey_update); + chip->m_stream = device->machine().sound().stream_alloc(*device, 0, 1, sample_rate, chip, pokey_update); register_for_save(chip, device); } @@ -815,7 +815,7 @@ WRITE8_DEVICE_HANDLER( pokey_w ) pokey_state *p = get_safe_token(device); int ch_mask = 0, new_val; - p->channel->update(); + p->m_stream->update(); /* determine which address was changed */ switch (offset & 15) @@ -908,15 +908,20 @@ WRITE8_DEVICE_HANDLER( pokey_w ) p->timer[TIMER4]->adjust(attotime::never, p->timer_param[TIMER4]); /* reset all counters to zero (side effect) */ - p->counter[CHAN1] = 0; - p->counter[CHAN2] = 0; - p->counter[CHAN3] = 0; - p->counter[CHAN4] = 0; + + reset_channel(p, CHAN1); + reset_channel(p, CHAN2); + reset_channel(p, CHAN3); + reset_channel(p, CHAN4); /* From the pokey documentation */ - p->output[CHAN1] = 1; - p->output[CHAN2] = 1; + p->output[CHAN1] = 0; + p->output[CHAN2] = 0; p->output[CHAN3] = 0; p->output[CHAN4] = 0; + p->filter_sample[CHAN1] = 1; + p->filter_sample[CHAN2] = 1; + p->filter_sample[CHAN3] = 0; + p->filter_sample[CHAN4] = 0; /* joined chan#1 and chan#2 ? */ if( p->AUDCTL & CH12_JOINED ) @@ -1228,3 +1233,926 @@ DEVICE_GET_INFO( pokey ) DEFINE_LEGACY_SOUND_DEVICE(POKEY, pokey); + + + + + + + + + + + + + + + + +// device type definition +const device_type POKEYN = &device_creator; + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// okim9810_device - constructor +//------------------------------------------------- + +pokeyn_device::pokeyn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, POKEYN, "POKEYN", tag, owner, clock), + device_sound_interface(mconfig, *this), + m_stream(NULL) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void pokeyn_device::device_start() +{ + //int sample_rate = clock(); + int i; + + if (static_config()) + memcpy(&m_intf, static_config(), sizeof(pokey_interface)); + m_clock_period = attotime::from_hz(clock()); + + /* calculate the A/D times + * In normal, slow mode (SKCTL bit SK_PADDLE is clear) the conversion + * takes N scanlines, where N is the paddle value. A single scanline + * takes approximately 64us to finish (1.78979MHz clock). + * In quick mode (SK_PADDLE set) the conversion is done very fast + * (takes two scanlines) but the result is not as accurate. + */ + + m_ad_time_fast = (attotime::from_nsec(64000*2/228) * FREQ_17_EXACT) / clock(); + m_ad_time_slow = (attotime::from_nsec(64000 ) * FREQ_17_EXACT) / clock(); + + /* initialize the poly counters */ + poly_init(m_poly4, 4, 3, 1, 0x00004); + poly_init(m_poly5, 5, 3, 2, 0x00008); + poly_init(m_poly9, 9, 8, 1, 0x00180); + poly_init(m_poly17, 17,16, 1, 0x1c000); + + /* initialize the random arrays */ + rand_init(m_rand9, 9, 8, 1, 0x00180); + rand_init(m_rand17, 17,16, 1, 0x1c000); + + m_divisor[CHAN1] = 4; + m_divisor[CHAN2] = 4; + m_divisor[CHAN3] = 4; + m_divisor[CHAN4] = 4; + m_clockmult = DIV_64; + m_KBCODE = 0x09; /* Atari 800 'no key' */ + m_SKCTL = SK_RESET; /* let the RNG run after reset */ + m_rtimer = timer_alloc(0); + + m_timer[0] = timer_alloc(1); + m_timer[1] = timer_alloc(1); + m_timer[2] = timer_alloc(1); + + for (i=0; i<8; i++) + { + m_ptimer[i] = timer_alloc(2); + m_pot_r[i].resolve(m_intf.pot_r[i], *this); + } + m_allpot_r.resolve(m_intf.allpot_r, *this); + m_serin_r.resolve(m_intf.serin_r, *this); + m_serout_w.resolve(m_intf.serout_w, *this); + m_interrupt_cb = m_intf.interrupt_cb; + + m_stream = stream_alloc(0, 1, clock()); + + save_item(NAME(m_counter)); + save_item(NAME(m_divisor)); + save_item(NAME(m_volume)); + save_item(NAME(m_output)); + save_item(NAME(m_filter_sample)); + save_item(NAME(m_clock_cnt)); + save_item(NAME(m_borrow_cnt)); + save_item(NAME(m_p4)); + save_item(NAME(m_p5)); + save_item(NAME(m_p9)); + save_item(NAME(m_p17)); + save_item(NAME(m_r9)); + save_item(NAME(m_r17)); + save_item(NAME(m_clockmult)); + save_item(NAME(m_timer_period[0])); + save_item(NAME(m_timer_period[1])); + save_item(NAME(m_timer_period[2])); + save_item(NAME(m_timer_param)); + save_item(NAME(m_AUDF)); + save_item(NAME(m_AUDC)); + save_item(NAME(m_POTx)); + save_item(NAME(m_AUDCTL)); + save_item(NAME(m_ALLPOT)); + save_item(NAME(m_KBCODE)); + save_item(NAME(m_RANDOM)); + save_item(NAME(m_SERIN)); + save_item(NAME(m_SEROUT)); + save_item(NAME(m_IRQST)); + save_item(NAME(m_IRQEN)); + save_item(NAME(m_SKSTAT)); + save_item(NAME(m_SKCTL));} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void pokeyn_device::device_reset() +{ + m_stream->update(); +} + + +//------------------------------------------------- +// device_post_load - device-specific post-load +//------------------------------------------------- + +void pokeyn_device::device_post_load() +{ +} + + +//------------------------------------------------- +// device_clock_changed - called if the clock +// changes +//------------------------------------------------- + +void pokeyn_device::device_clock_changed() +{ +} + +//------------------------------------------------- +// stream_generate - handle update requests for +// our sound stream +//------------------------------------------------- + +void pokeyn_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == 1) + { + int timers = param; + + LOG_TIMER(("POKEY #%p timer %d with IRQEN $%02x\n", this, timers, m_IRQEN)); + + /* check if some of the requested timer interrupts are enabled */ + timers &= m_IRQEN; + + if( timers ) + { + /* set the enabled timer irq status bits */ + m_IRQST |= timers; + /* call back an application supplied function to handle the interrupt */ + if( m_interrupt_cb ) + (*m_interrupt_cb)(this, timers); + } + } + else if (id == 2) + { + int pot = param; + + LOG(("POKEY #%p POT%d triggers after %dus\n", this, pot, (int)(1000000 * m_ptimer[pot]->elapsed().as_double()))); + m_ALLPOT &= ~(1 << pot); /* set the enabled timer irq status bits */ + + } + else if (id == 3) + { + /* serout_ready_cb */ + if( m_IRQEN & IRQ_SEROR ) + { + m_IRQST |= IRQ_SEROR; + if( m_interrupt_cb ) + (m_interrupt_cb)(this, IRQ_SEROR); + } + } + else if (id == 4) + { + /* serout_complete */ + if( m_IRQEN & IRQ_SEROC ) + { + m_IRQST |= IRQ_SEROC; + if( m_interrupt_cb ) + (m_interrupt_cb)(this, IRQ_SEROC); + } + } + else + assert_always(FALSE, "Unknown id in pokey_device::device_timer"); +} + +//------------------------------------------------- +// stream_generate - handle update requests for +// our sound stream +//------------------------------------------------- + +/* + * http://www.atariage.com/forums/topic/3328-sio-protocol/page__st__100#entry1680190: + * I noticed that the Pokey counters have clocked carry (actually, "borrow") positions that delay the + * counter by 3 cycles, plus the 1 reset clock. So 16 bit mode has 6 carry delays and a reset clock. + * I'm sure this was done because the propagation delays limited the number of cells the subtraction could ripple though. + * + */ + +void pokeyn_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + stream_sample_t *buffer = outputs[0]; + int base_clock = (m_AUDCTL & CLK_15KHZ) ? CLK_114 : CLK_28; + + while( samples > 0 ) + { + int ch, clk; + UINT32 sum = 0; + int clock_triggered[3] = {0,0,0}; + + for (clk = 0; clk < 3; clk++) + { + m_clock_cnt[clk]++; + if (m_clock_cnt[clk] >= clock_divisors[clk]) + { + m_clock_cnt[clk] = 0; + clock_triggered[clk] = 1; + } + } + + m_p4 = (m_p4 + 1) % 0x0000f; + m_p5 = (m_p5 + 1) % 0x0001f; + m_p9 = (m_p9 + 1) % 0x001ff; + m_p17 = (m_p17 + 1 ) % 0x1ffff; + + + clk = (m_AUDCTL & CH1_HICLK) ? CLK_1 : base_clock; + if (clock_triggered[clk]) + inc_chan(CHAN1); + + clk = (m_AUDCTL & CH3_HICLK) ? CLK_1 : base_clock; + if (clock_triggered[clk]) + inc_chan(CHAN3); + + if (clock_triggered[base_clock]) + { + if (!(m_AUDCTL & CH12_JOINED)) + inc_chan(CHAN2); + if (!(m_AUDCTL & CH34_JOINED)) + inc_chan(CHAN4); + } + + /* do CHAN2 before CHAN1 because CHAN1 may set borrow! */ + if (check_borrow(CHAN2)) + { + int isJoined = (m_AUDCTL & CH12_JOINED); + if (isJoined) + reset_channel(CHAN1); + reset_channel(CHAN2); + process_channel(CHAN2); + } + + if (check_borrow(CHAN1)) + { + int isJoined = (m_AUDCTL & CH12_JOINED); + if (isJoined) + inc_chan(CHAN2); + else + reset_channel(CHAN1); + process_channel(CHAN1); + } + + /* do CHAN4 before CHAN3 because CHAN3 may set borrow! */ + if (check_borrow(CHAN4)) + { + int isJoined = (m_AUDCTL & CH34_JOINED); + if (isJoined) + reset_channel(CHAN3); + reset_channel(CHAN4); + process_channel(CHAN4); + /* is this a filtering channel (3/4) and is the filter active? */ + if (m_AUDCTL & CH2_FILTER) + m_filter_sample[CHAN2] = m_output[CHAN2]; + else + m_filter_sample[CHAN2] = 1; + } + + if (check_borrow(CHAN3)) + { + int isJoined = (m_AUDCTL & CH34_JOINED); + if (isJoined) + inc_chan(CHAN4); + else + reset_channel(CHAN3); + process_channel(CHAN3); + /* is this a filtering channel (3/4) and is the filter active? */ + if (m_AUDCTL & CH1_FILTER) + m_filter_sample[CHAN1] = m_output[CHAN1]; + else + m_filter_sample[CHAN1] = 1; + } + + for (ch = 0; ch < 4; ch++) + { + sum += (((m_output[ch] ^ m_filter_sample[ch]) || (m_AUDC[ch] & VOLUME_ONLY)) ? m_volume[ch] : 0 ); + } + + /* store sum of output signals into the buffer */ + *buffer++ = (sum > 0x7fff) ? 0x7fff : sum; + samples--; + + } + m_rtimer->adjust(attotime::never); + +} + + + +//------------------------------------------------- +// read - memory interface for reading the active status +//------------------------------------------------- + +READ8_MEMBER( pokeyn_device::read ) +{ + int data = 0, pot; + UINT32 adjust = 0; + + switch (offset & 15) + { + case POT0_C: case POT1_C: case POT2_C: case POT3_C: + case POT4_C: case POT5_C: case POT6_C: case POT7_C: + pot = offset & 7; + if( !m_pot_r[pot].isnull() ) + { + /* + * If the conversion is not yet finished (ptimer running), + * get the current value by the linear interpolation of + * the final value using the elapsed time. + */ + if( m_ALLPOT & (1 << pot) ) + { + data = m_ptimer[pot]->elapsed().attoseconds / ((m_SKCTL & SK_PADDLE) ? m_ad_time_fast : m_ad_time_slow).attoseconds; + LOG(("POKEY '%s' read POT%d (interpolated) $%02x\n", tag(), pot, data)); + } + else + { + data = m_POTx[pot]; + LOG(("POKEY '%s' read POT%d (final value) $%02x\n", tag(), pot, data)); + } + } + else + logerror("%s: warning - read '%s' POT%d\n", machine().describe_context(), tag(), pot); + break; + + case ALLPOT_C: + /**************************************************************** + * If the 2 least significant bits of SKCTL are 0, the ALLPOTs + * are disabled (SKRESET). Thanks to MikeJ for pointing this out. + ****************************************************************/ + if( (m_SKCTL & SK_RESET) == 0) + { + data = 0; + LOG(("POKEY '%s' ALLPOT internal $%02x (reset)\n", tag(), data)); + } + else if( !m_allpot_r.isnull() ) + { + data = m_allpot_r(offset); + LOG(("POKEY '%s' ALLPOT callback $%02x\n", tag(), data)); + } + else + { + data = m_ALLPOT; + LOG(("POKEY '%s' ALLPOT internal $%02x\n", tag(), data)); + } + break; + + case KBCODE_C: + data = m_KBCODE; + break; + + case RANDOM_C: + /**************************************************************** + * If the 2 least significant bits of SKCTL are 0, the random + * number generator is disabled (SKRESET). Thanks to Eric Smith + * for pointing out this critical bit of info! If the random + * number generator is enabled, get a new random number. Take + * the time gone since the last read into account and read the + * new value from an appropriate offset in the rand17 table. + ****************************************************************/ + if( m_SKCTL & SK_RESET ) + { + adjust = m_rtimer->elapsed().as_double() / m_clock_period.as_double(); + m_r9 = (m_r9 + adjust) % 0x001ff; + m_r17 = (m_r17 + adjust) % 0x1ffff; + } + else + { + adjust = 1; + m_r9 = 0; + m_r17 = 0; + LOG_RAND(("POKEY '%s' rand17 frozen (SKCTL): $%02x\n", tag(), m_RANDOM)); + } + if( m_AUDCTL & POLY9 ) + { + m_RANDOM = m_rand9[m_r9]; + LOG_RAND(("POKEY '%s' adjust %u rand9[$%05x]: $%02x\n", tag(), adjust, m_r9, m_RANDOM)); + } + else + { + m_RANDOM = m_rand17[m_r17]; + LOG_RAND(("POKEY '%s' adjust %u rand17[$%05x]: $%02x\n", tag(), adjust, m_r17, m_RANDOM)); + } + if (adjust > 0) + m_rtimer->adjust(attotime::never); + data = m_RANDOM ^ 0xff; + break; + + case SERIN_C: + if( !m_serin_r.isnull() ) + m_SERIN = m_serin_r(offset); + data = m_SERIN; + LOG(("POKEY '%s' SERIN $%02x\n", tag(), 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)); + break; + + case SKSTAT_C: + /* SKSTAT is also an active low input port */ + data = m_SKSTAT ^ 0xff; + LOG(("POKEY '%s' SKSTAT $%02x\n", tag(), data)); + break; + + default: + LOG(("POKEY '%s' register $%02x\n", tag(), offset)); + break; + } + return data; + +} + + +//------------------------------------------------- +// write - memory interface for write +//------------------------------------------------- + +WRITE8_MEMBER( pokeyn_device::write ) +{ + write_cmd(offset, data); +} + +void pokeyn_device::write_cmd(int offset, UINT8 data) +{ + int ch_mask = 0, new_val; + + m_stream->update(); + + /* determine which address was changed */ + switch (offset & 15) + { + case AUDF1_C: + if( data == m_AUDF[CHAN1] ) + return; + LOG_SOUND(("POKEY '%s' AUDF1 $%02x\n", tag(), data)); + m_AUDF[CHAN1] = data; + ch_mask = 1 << CHAN1; + if( m_AUDCTL & CH12_JOINED ) /* if ch 1&2 tied together */ + ch_mask |= 1 << CHAN2; /* then also change on ch2 */ + break; + + case AUDC1_C: + if( data == m_AUDC[CHAN1] ) + return; + LOG_SOUND(("POKEY '%s' AUDC1 $%02x (%s)\n", tag(), data, audc2str(data))); + m_AUDC[CHAN1] = data; + ch_mask = 1 << CHAN1; + break; + + case AUDF2_C: + if( data == m_AUDF[CHAN2] ) + return; + LOG_SOUND(("POKEY '%s' AUDF2 $%02x\n", tag(), data)); + m_AUDF[CHAN2] = data; + ch_mask = 1 << CHAN2; + break; + + case AUDC2_C: + if( data == m_AUDC[CHAN2] ) + return; + LOG_SOUND(("POKEY '%s' AUDC2 $%02x (%s)\n", tag(), data, audc2str(data))); + m_AUDC[CHAN2] = data; + ch_mask = 1 << CHAN2; + break; + + case AUDF3_C: + if( data == m_AUDF[CHAN3] ) + return; + LOG_SOUND(("POKEY '%s' AUDF3 $%02x\n", tag(), data)); + m_AUDF[CHAN3] = data; + ch_mask = 1 << CHAN3; + + if( m_AUDCTL & CH34_JOINED ) /* if ch 3&4 tied together */ + ch_mask |= 1 << CHAN4; /* then also change on ch4 */ + break; + + case AUDC3_C: + if( data == m_AUDC[CHAN3] ) + return; + LOG_SOUND(("POKEY '%s' AUDC3 $%02x (%s)\n", tag(), data, audc2str(data))); + m_AUDC[CHAN3] = data; + ch_mask = 1 << CHAN3; + break; + + case AUDF4_C: + if( data == m_AUDF[CHAN4] ) + return; + LOG_SOUND(("POKEY '%s' AUDF4 $%02x\n", tag(), data)); + m_AUDF[CHAN4] = data; + ch_mask = 1 << CHAN4; + break; + + case AUDC4_C: + if( data == m_AUDC[CHAN4] ) + return; + LOG_SOUND(("POKEY '%s' AUDC4 $%02x (%s)\n", tag(), data, audc2str(data))); + m_AUDC[CHAN4] = data; + ch_mask = 1 << CHAN4; + break; + + case AUDCTL_C: + if( data == m_AUDCTL ) + return; + LOG_SOUND(("POKEY '%s' AUDCTL $%02x (%s)\n", tag(), data, audctl2str(data))); + m_AUDCTL = data; + ch_mask = 15; /* all channels */ + /* determine the base multiplier for the 'div by n' calculations */ + m_clockmult = (m_AUDCTL & CLK_15KHZ) ? DIV_15 : DIV_64; + break; + + case STIMER_C: + /* first remove any existing timers */ + LOG_TIMER(("POKEY '%s' STIMER $%02x\n", tag(), data)); + + m_timer[TIMER1]->adjust(attotime::never, m_timer_param[TIMER1]); + m_timer[TIMER2]->adjust(attotime::never, m_timer_param[TIMER2]); + m_timer[TIMER4]->adjust(attotime::never, m_timer_param[TIMER4]); + + /* reset all counters to zero (side effect) */ + reset_channel(CHAN1); + reset_channel(CHAN2); + reset_channel(CHAN3); + reset_channel(CHAN4); + /* From the pokey documentation */ + m_output[CHAN1] = 0; + m_output[CHAN2] = 0; + m_output[CHAN3] = 0; + m_output[CHAN4] = 0; + m_filter_sample[CHAN1] = 1; + m_filter_sample[CHAN2] = 1; + m_filter_sample[CHAN3] = 0; + m_filter_sample[CHAN4] = 0; + /* joined chan#1 and chan#2 ? */ + if( m_AUDCTL & CH12_JOINED ) + { + if( m_divisor[CHAN2] > 4 ) + { + LOG_TIMER(("POKEY '%s' timer1+2 after %d clocks\n", tag(), m_divisor[CHAN2])); + /* set timer #1 _and_ #2 event after timer_div clocks of joined CHAN1+CHAN2 */ + m_timer_period[TIMER2] = m_clock_period * m_divisor[CHAN2]; + m_timer_param[TIMER2] = IRQ_TIMR2|IRQ_TIMR1; + m_timer[TIMER2]->adjust(m_timer_period[TIMER2], m_timer_param[TIMER2], m_timer_period[TIMER2]); + } + } + else + { + if( m_divisor[CHAN1] > 4 ) + { + LOG_TIMER(("POKEY '%s' timer1 after %d clocks\n", tag(), m_divisor[CHAN1])); + /* set timer #1 event after timer_div clocks of CHAN1 */ + m_timer_period[TIMER1] = m_clock_period * m_divisor[CHAN1]; + m_timer_param[TIMER1] = IRQ_TIMR1; + m_timer[TIMER1]->adjust(m_timer_period[TIMER1], m_timer_param[TIMER1], m_timer_period[TIMER1]); + } + + if( m_divisor[CHAN2] > 4 ) + { + LOG_TIMER(("POKEY '%s' timer2 after %d clocks\n", tag(), m_divisor[CHAN2])); + /* set timer #2 event after timer_div clocks of CHAN2 */ + m_timer_period[TIMER2] = m_clock_period * m_divisor[CHAN2]; + m_timer_param[TIMER2] = IRQ_TIMR2; + m_timer[TIMER2]->adjust(m_timer_period[TIMER2], m_timer_param[TIMER2], m_timer_period[TIMER2]); + } + } + + /* Note: p[chip] does not have a timer #3 */ + + if( m_AUDCTL & CH34_JOINED ) + { + /* not sure about this: if audc4 == 0000xxxx don't start timer 4 ? */ + if( m_AUDC[CHAN4] & 0xf0 ) + { + if( m_divisor[CHAN4] > 4 ) + { + LOG_TIMER(("POKEY '%s' timer4 after %d clocks\n", tag(), m_divisor[CHAN4])); + /* set timer #4 event after timer_div clocks of CHAN4 */ + m_timer_period[TIMER4] = m_clock_period * m_divisor[CHAN4]; + m_timer_param[TIMER4] = IRQ_TIMR4; + m_timer[TIMER4]->adjust(m_timer_period[TIMER4], m_timer_param[TIMER4], m_timer_period[TIMER4]); + } + } + } + else + { + if( m_divisor[CHAN4] > 4 ) + { + LOG_TIMER(("POKEY '%s' timer4 after %d clocks\n", tag(), m_divisor[CHAN4])); + /* set timer #4 event after timer_div clocks of CHAN4 */ + m_timer_period[TIMER4] = m_clock_period * m_divisor[CHAN4]; + m_timer_param[TIMER4] = IRQ_TIMR4; + m_timer[TIMER4]->adjust(m_timer_period[TIMER4], m_timer_param[TIMER4], m_timer_period[TIMER4]); + } + } + + m_timer[TIMER1]->enable(m_IRQEN & IRQ_TIMR1); + m_timer[TIMER2]->enable(m_IRQEN & IRQ_TIMR2); + m_timer[TIMER4]->enable(m_IRQEN & IRQ_TIMR4); + break; + + case SKREST_C: + /* reset SKSTAT */ + LOG(("POKEY '%s' SKREST $%02x\n", tag(), data)); + m_SKSTAT &= ~(SK_FRAME|SK_OVERRUN|SK_KBERR); + break; + + case POTGO_C: + LOG(("POKEY '%s' POTGO $%02x\n", tag(), data)); + pokey_potgo(); + break; + + case SEROUT_C: + LOG(("POKEY '%s' SEROUT $%02x\n", tag(), data)); + m_serout_w(offset, data); + m_SKSTAT |= SK_SEROUT; + /* + * These are arbitrary values, tested with some custom boot + * loaders from Ballblazer and Escape from Fractalus + * The real times are unknown + */ + timer_set(attotime::from_usec(200), 3); + /* 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); + break; + + case IRQEN_C: + LOG(("POKEY '%s' IRQEN $%02x\n", tag(), data)); + + /* acknowledge one or more IRQST bits ? */ + if( m_IRQST & ~data ) + { + /* reset IRQST bits that are masked now */ + m_IRQST &= data; + } + else + { + /* enable/disable timers now to avoid unneeded + breaking of the CPU cores for masked timers */ + if( m_timer[TIMER1] && ((m_IRQEN^data) & IRQ_TIMR1) ) + m_timer[TIMER1]->enable(data & IRQ_TIMR1); + if( m_timer[TIMER2] && ((m_IRQEN^data) & IRQ_TIMR2) ) + m_timer[TIMER2]->enable(data & IRQ_TIMR2); + if( m_timer[TIMER4] && ((m_IRQEN^data) & IRQ_TIMR4) ) + m_timer[TIMER4]->enable(data & IRQ_TIMR4); + } + /* store irq enable */ + m_IRQEN = data; + break; + + case SKCTL_C: + if( data == m_SKCTL ) + return; + LOG(("POKEY '%s' SKCTL $%02x\n", tag(), data)); + m_SKCTL = data; + if( !(data & SK_RESET) ) + { + write_cmd(IRQEN_C, 0); + write_cmd(SKREST_C, 0); + } + break; + } + + /************************************************************ + * As defined in the manual, the exact counter values are + * different depending on the frequency and resolution: + * 64 kHz or 15 kHz - AUDF + 1 + * 1.79 MHz, 8-bit - AUDF + 4 + * 1.79 MHz, 16-bit - AUDF[CHAN1]+256*AUDF[CHAN2] + 7 + ************************************************************/ + + /* only reset the channels that have changed */ + + if( ch_mask & (1 << CHAN1) ) + { + /* process channel 1 frequency */ + if( m_AUDCTL & CH1_HICLK ) + new_val = m_AUDF[CHAN1] + DIVADD_HICLK; + else + new_val = (m_AUDF[CHAN1] + DIVADD_LOCLK) * m_clockmult; + + LOG_SOUND(("POKEY '%s' chan1 %d\n", tag(), new_val)); + + m_volume[CHAN1] = (m_AUDC[CHAN1] & VOLUME_MASK) * POKEY_DEFAULT_GAIN; + m_divisor[CHAN1] = new_val; + if( m_interrupt_cb && m_timer[TIMER1] ) + m_timer[TIMER1]->adjust(m_clock_period * new_val, m_timer_param[TIMER1], m_timer_period[TIMER1]); + } + + if( ch_mask & (1 << CHAN2) ) + { + /* process channel 2 frequency */ + if( m_AUDCTL & CH12_JOINED ) + { + if( m_AUDCTL & CH1_HICLK ) + new_val = m_AUDF[CHAN2] * 256 + m_AUDF[CHAN1] + DIVADD_HICLK_JOINED; + else + new_val = (m_AUDF[CHAN2] * 256 + m_AUDF[CHAN1] + DIVADD_LOCLK) * m_clockmult; + LOG_SOUND(("POKEY '%s' chan1+2 %d\n", tag(), new_val)); + } + else + { + new_val = (m_AUDF[CHAN2] + DIVADD_LOCLK) * m_clockmult; + LOG_SOUND(("POKEY '%s' chan2 %d\n", tag(), new_val)); + } + + m_volume[CHAN2] = (m_AUDC[CHAN2] & VOLUME_MASK) * POKEY_DEFAULT_GAIN; + m_divisor[CHAN2] = new_val; + if( m_interrupt_cb && m_timer[TIMER2] ) + m_timer[TIMER2]->adjust(m_clock_period * new_val, m_timer_param[TIMER2], m_timer_period[TIMER2]); + } + + if( ch_mask & (1 << CHAN3) ) + { + /* process channel 3 frequency */ + if( m_AUDCTL & CH3_HICLK ) + new_val = m_AUDF[CHAN3] + DIVADD_HICLK; + else + new_val = (m_AUDF[CHAN3] + DIVADD_LOCLK) * m_clockmult; + + LOG_SOUND(("POKEY '%s' chan3 %d\n", tag(), new_val)); + + m_volume[CHAN3] = (m_AUDC[CHAN3] & VOLUME_MASK) * POKEY_DEFAULT_GAIN; + m_divisor[CHAN3] = new_val; + /* channel 3 does not have a timer associated */ + } + + if( ch_mask & (1 << CHAN4) ) + { + /* process channel 4 frequency */ + if( m_AUDCTL & CH34_JOINED ) + { + if( m_AUDCTL & CH3_HICLK ) + new_val = m_AUDF[CHAN4] * 256 + m_AUDF[CHAN3] + DIVADD_HICLK_JOINED; + else + new_val = (m_AUDF[CHAN4] * 256 + m_AUDF[CHAN3] + DIVADD_LOCLK) * m_clockmult; + LOG_SOUND(("POKEY '%s' chan3+4 %d\n", tag(), new_val)); + } + else + { + new_val = (m_AUDF[CHAN4] + DIVADD_LOCLK) * m_clockmult; + LOG_SOUND(("POKEY '%s' chan4 %d\n", tag(), new_val)); + } + + m_volume[CHAN4] = (m_AUDC[CHAN4] & VOLUME_MASK) * POKEY_DEFAULT_GAIN; + m_divisor[CHAN4] = new_val; + if( m_interrupt_cb && m_timer[TIMER4] ) + m_timer[TIMER4]->adjust(m_clock_period * new_val, m_timer_param[TIMER4], m_timer_period[TIMER4]); + } +} + + +//------------------------------------------------- +// private stuff +//------------------------------------------------- + +inline void pokeyn_device::process_channel(int ch) +{ + int toggle = 0; + + if( (m_AUDC[ch] & NOTPOLY5) || m_poly5[m_p5] ) + { + if( m_AUDC[ch] & PURE ) + toggle = 1; + else + if( m_AUDC[ch] & POLY4 ) + toggle = m_output[ch] == !m_poly4[m_p4]; + else + if( m_AUDCTL & POLY9 ) + toggle = m_output[ch] == !m_poly9[m_p9]; + else + toggle = m_output[ch] == !m_poly17[m_p17]; + } + if( toggle ) + { + m_output[ch] ^= 1; + } + /* is this a filtering channel (3/4) and is the filter active? */ + + if( m_AUDCTL & ((CH1_FILTER|CH2_FILTER) & (0x10 >> ch)) ) + { + if( ch >= 2) + { + m_filter_sample[ch-2] = m_output[ch-2]; + } + } + +} + + +inline void pokeyn_device::reset_channel(int ch) +{ + m_counter[ch] = m_AUDF[ch] ^ 0xff; +} + + +inline void pokeyn_device::inc_chan(int ch) +{ + m_counter[ch] = (m_counter[ch] + 1) & 0xff; + if (m_counter[ch] == 0 && m_borrow_cnt[ch] == 0) + m_borrow_cnt[ch] = 3; +} + +inline int pokeyn_device::check_borrow(int ch) +{ + if (m_borrow_cnt[ch] > 0) + { + m_borrow_cnt[ch]--; + return (m_borrow_cnt[ch] == 0); + } + return 0; +} + +void pokeyn_device::pokey_potgo(void) +{ + int pot; + + LOG(("POKEY #%p pokey_potgo\n", this)); + + m_ALLPOT = 0xff; + + for( pot = 0; pot < 8; pot++ ) + { + m_POTx[pot] = 0xff; + if( !m_pot_r[pot].isnull() ) + { + int r = m_pot_r[pot](pot); + + LOG(("POKEY %s pot_r(%d) returned $%02x\n", tag(), pot, r)); + if( r != -1 ) + { + if (r > 228) + r = 228; + + /* final value */ + m_POTx[pot] = r; + m_ptimer[pot]->adjust(((m_SKCTL & SK_PADDLE) ? m_ad_time_fast : m_ad_time_slow) * r, pot); + } + } + } +} + + +void pokeyn_device::poly_init(UINT8 *poly, int size, int left, int right, int add) +{ + int mask = (1 << size) - 1; + int i, x = 0; + + LOG_POLY(("poly %d\n", size)); + for( i = 0; i < mask; i++ ) + { + *poly++ = x & 1; + LOG_POLY(("%05x: %d\n", x, x&1)); + /* calculate next bit */ + x = ((x << left) + (x >> right) + add) & mask; + } +} + +void pokeyn_device::rand_init(UINT8 *rng, int size, int left, int right, int add) +{ + int mask = (1 << size) - 1; + int i, x = 0; + + LOG_RAND(("rand %d\n", size)); + for( i = 0; i < mask; i++ ) + { + if (size == 17) + *rng = x >> 6; /* use bits 6..13 */ + else + *rng = x; /* use bits 0..7 */ + LOG_RAND(("%05x: %02x\n", x, *rng)); + rng++; + /* calculate next bit */ + x = ((x << left) + (x >> right) + add) & mask; + } +} diff --git a/src/emu/sound/pokey.h b/src/emu/sound/pokey.h index 39ac7f4bcb6..481ca3b6488 100644 --- a/src/emu/sound/pokey.h +++ b/src/emu/sound/pokey.h @@ -92,4 +92,108 @@ void pokey_kbcode_w (device_t *device, int kbcode, int make); DECLARE_LEGACY_SOUND_DEVICE(POKEY, pokey); +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + + +// ======================> pokey_device + +class pokeyn_device : public device_t, + public device_sound_interface +{ +public: + // construction/destruction + pokeyn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + DECLARE_READ8_MEMBER( read ); + DECLARE_WRITE8_MEMBER( write ); + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual void device_post_load(); + virtual void device_clock_changed(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + + // device_sound_interface overrides + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); + + // configuration state + pokey_interface m_intf; + +private: + + void poly_init(UINT8 *poly, int size, int left, int right, int add); + void rand_init(UINT8 *rng, int size, int left, int right, int add); + inline void process_channel(int ch); + inline void reset_channel(int ch); + inline void inc_chan(int ch); + inline int check_borrow(int ch); + void pokey_potgo(void); + void write_cmd(int offset, UINT8 data); + + + + // internal state + sound_stream* m_stream; + + INT32 m_clock_cnt[3]; /* clock counters */ + INT32 m_borrow_cnt[4]; /* borrow counters */ + + INT32 m_counter[4]; /* channel counter */ + INT32 m_divisor[4]; /* channel divisor (modulo value) */ + UINT32 m_volume[4]; /* channel volume - derived */ + UINT8 m_output[4]; /* channel output signal (1 active, 0 inactive) */ + UINT8 m_filter_sample[4]; /* hi-pass filter sample */ + UINT32 m_p4; /* poly4 index */ + UINT32 m_p5; /* poly5 index */ + UINT32 m_p9; /* poly9 index */ + UINT32 m_p17; /* poly17 index */ + UINT32 m_r9; /* rand9 index */ + UINT32 m_r17; /* rand17 index */ + UINT32 m_clockmult; /* clock multiplier */ + emu_timer *m_timer[3]; /* timers for channel 1,2 and 4 events */ + attotime m_timer_period[3]; /* computed periods for these timers */ + int m_timer_param[3]; /* computed parameters for these timers */ + emu_timer *m_rtimer; /* timer for calculating the random offset */ + emu_timer *m_ptimer[8]; /* pot timers */ + devcb_resolved_read8 m_pot_r[8]; + devcb_resolved_read8 m_allpot_r; + devcb_resolved_read8 m_serin_r; + devcb_resolved_write8 m_serout_w; + void (*m_interrupt_cb)(device_t *device, int mask); + UINT8 m_AUDF[4]; /* AUDFx (D200, D202, D204, D206) */ + UINT8 m_AUDC[4]; /* AUDCx (D201, D203, D205, D207) */ + UINT8 m_POTx[8]; /* POTx (R/D200-D207) */ + UINT8 m_AUDCTL; /* AUDCTL (W/D208) */ + UINT8 m_ALLPOT; /* ALLPOT (R/D208) */ + UINT8 m_KBCODE; /* KBCODE (R/D209) */ + UINT8 m_RANDOM; /* RANDOM (R/D20A) */ + UINT8 m_SERIN; /* SERIN (R/D20D) */ + UINT8 m_SEROUT; /* SEROUT (W/D20D) */ + UINT8 m_IRQST; /* IRQST (R/D20E) */ + UINT8 m_IRQEN; /* IRQEN (W/D20E) */ + UINT8 m_SKSTAT; /* SKSTAT (R/D20F) */ + UINT8 m_SKCTL; /* SKCTL (W/D20F) */ + attotime m_clock_period; + attotime m_ad_time_fast; + attotime m_ad_time_slow; + + UINT8 m_poly4[0x0f]; + UINT8 m_poly5[0x1f]; + UINT8 m_poly9[0x1ff]; + UINT8 m_poly17[0x1ffff]; + + UINT8 m_rand9[0x1ff]; + UINT8 m_rand17[0x1ffff]; + +}; + + +// device type definition +extern const device_type POKEYN; + + #endif /* __POKEY_H__ */ diff --git a/src/mame/audio/bzone.c b/src/mame/audio/bzone.c index 74bf7a873ad..771a791f247 100644 --- a/src/mame/audio/bzone.c +++ b/src/mame/audio/bzone.c @@ -400,7 +400,7 @@ MACHINE_CONFIG_FRAGMENT( bzone_audio ) MCFG_SPEAKER_STANDARD_MONO("mono") - MCFG_SOUND_ADD("pokey", POKEY, BZONE_MASTER_CLOCK / 8) + MCFG_SOUND_ADD("pokey", POKEYN, BZONE_MASTER_CLOCK / 8) MCFG_SOUND_CONFIG(bzone_pokey_interface) MCFG_SOUND_ROUTE_EX(0, "discrete", 1.0, 0) diff --git a/src/mame/drivers/bzone.c b/src/mame/drivers/bzone.c index c2a34c7c15e..004d12b3a76 100644 --- a/src/mame/drivers/bzone.c +++ b/src/mame/drivers/bzone.c @@ -309,7 +309,8 @@ static ADDRESS_MAP_START( bzone_map, AS_PROGRAM, 8, bzone_state ) AM_RANGE(0x1800, 0x1800) AM_DEVREAD_LEGACY("mathbox", mathbox_status_r) AM_RANGE(0x1810, 0x1810) AM_DEVREAD_LEGACY("mathbox", mathbox_lo_r) AM_RANGE(0x1818, 0x1818) AM_DEVREAD_LEGACY("mathbox", mathbox_hi_r) - AM_RANGE(0x1820, 0x182f) AM_DEVREADWRITE_LEGACY("pokey", pokey_r, pokey_w) +// AM_RANGE(0x1820, 0x182f) AM_DEVREADWRITE_LEGACY("pokey", pokey_r, pokey_w) + AM_RANGE(0x1820, 0x182f) AM_DEVREADWRITE("pokey", pokeyn_device, read, write) AM_RANGE(0x1840, 0x1840) AM_DEVWRITE_LEGACY("discrete", bzone_sounds_w) AM_RANGE(0x1860, 0x187f) AM_DEVWRITE_LEGACY("mathbox", mathbox_go_w) AM_RANGE(0x2000, 0x2fff) AM_RAM AM_BASE_LEGACY(&avgdvg_vectorram) AM_SIZE_LEGACY(&avgdvg_vectorram_size) AM_REGION("maincpu", 0x2000) @@ -333,7 +334,8 @@ static ADDRESS_MAP_START( redbaron_map, AS_PROGRAM, 8, bzone_state ) AM_RANGE(0x1808, 0x1808) AM_DEVWRITE_LEGACY("custom", redbaron_joysound_w) /* and select joystick pot also */ AM_RANGE(0x180a, 0x180a) AM_WRITENOP /* sound reset, yet todo */ AM_RANGE(0x180c, 0x180c) AM_DEVWRITE("earom", atari_vg_earom_device, ctrl_w) - AM_RANGE(0x1810, 0x181f) AM_DEVREADWRITE_LEGACY("pokey", pokey_r, pokey_w) + //AM_RANGE(0x1810, 0x181f) AM_DEVREADWRITE_LEGACY("pokey", pokey_r, pokey_w) + AM_RANGE(0x1820, 0x182f) AM_DEVREADWRITE("pokey", pokeyn_device, read, write) AM_RANGE(0x1820, 0x185f) AM_DEVREADWRITE("earom", atari_vg_earom_device, read, write) AM_RANGE(0x1860, 0x187f) AM_DEVWRITE_LEGACY("mathbox", mathbox_go_w) AM_RANGE(0x2000, 0x2fff) AM_RAM AM_BASE_LEGACY(&avgdvg_vectorram) AM_SIZE_LEGACY(&avgdvg_vectorram_size) AM_REGION("maincpu", 0x2000) @@ -600,7 +602,7 @@ static MACHINE_CONFIG_DERIVED( redbaron, bzone_base ) /* sound hardware */ MCFG_SPEAKER_STANDARD_MONO("mono") - MCFG_SOUND_ADD("pokey", POKEY, 1500000) + MCFG_SOUND_ADD("pokey", POKEYN, 1500000) MCFG_SOUND_CONFIG(redbaron_pokey_interface) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0) diff --git a/src/mame/drivers/ccastles.c b/src/mame/drivers/ccastles.c index 625bf884bf8..cbacc30c26f 100644 --- a/src/mame/drivers/ccastles.c +++ b/src/mame/drivers/ccastles.c @@ -333,8 +333,8 @@ static ADDRESS_MAP_START( main_map, AS_PROGRAM, 8, ccastles_state ) AM_RANGE(0x9000, 0x90ff) AM_MIRROR(0x0300) AM_READWRITE(nvram_r, nvram_w) AM_RANGE(0x9400, 0x9403) AM_MIRROR(0x01fc) AM_READ(leta_r) AM_RANGE(0x9600, 0x97ff) AM_READ_PORT("IN0") - AM_RANGE(0x9800, 0x980f) AM_MIRROR(0x01f0) AM_DEVREADWRITE_LEGACY("pokey1", pokey_r, pokey_w) - AM_RANGE(0x9a00, 0x9a0f) AM_MIRROR(0x01f0) AM_DEVREADWRITE_LEGACY("pokey2", pokey_r, pokey_w) + AM_RANGE(0x9800, 0x980f) AM_MIRROR(0x01f0) AM_DEVREADWRITE("pokey1", pokeyn_device, read, write) + AM_RANGE(0x9a00, 0x9a0f) AM_MIRROR(0x01f0) AM_DEVREADWRITE("pokey2", pokeyn_device, read, write) AM_RANGE(0x9c00, 0x9c7f) AM_WRITE(nvram_recall_w) AM_RANGE(0x9c80, 0x9cff) AM_WRITE(ccastles_hscroll_w) AM_RANGE(0x9d00, 0x9d7f) AM_WRITE(ccastles_vscroll_w) @@ -495,10 +495,10 @@ static MACHINE_CONFIG_START( ccastles, ccastles_state ) /* sound hardware */ MCFG_SPEAKER_STANDARD_MONO("mono") - MCFG_SOUND_ADD("pokey1", POKEY, MASTER_CLOCK/8) + MCFG_SOUND_ADD("pokey1", POKEYN, MASTER_CLOCK/8) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) - MCFG_SOUND_ADD("pokey2", POKEY, MASTER_CLOCK/8) + MCFG_SOUND_ADD("pokey2", POKEYN, MASTER_CLOCK/8) MCFG_SOUND_CONFIG(pokey_config) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) MACHINE_CONFIG_END diff --git a/src/mame/drivers/missile.c b/src/mame/drivers/missile.c index 9073c8f29f1..ae7befe2d93 100644 --- a/src/mame/drivers/missile.c +++ b/src/mame/drivers/missile.c @@ -714,6 +714,7 @@ static SCREEN_UPDATE_IND16( missile ) WRITE8_MEMBER(missile_state::missile_w) { UINT8 *videoram = m_videoram; + /* if we're in MADSEL mode, write to video RAM */ if (get_madsel(&space)) { @@ -730,7 +731,10 @@ WRITE8_MEMBER(missile_state::missile_w) /* POKEY */ else if (offset < 0x4800) - pokey_w(machine().device("pokey"), offset & 0x0f, data); + { + pokeyn_device *pokey_dev = downcast(machine().device("pokey")); + pokey_dev->write(*machine().firstcpu->space(), offset, data, 0xff); + } /* OUT0 */ else if (offset < 0x4900) @@ -790,7 +794,10 @@ READ8_MEMBER(missile_state::missile_r) /* POKEY */ else if (offset < 0x4800) - result = pokey_r(machine().device("pokey"), offset & 0x0f); + { + pokeyn_device *pokey_dev = downcast(machine().device("pokey")); + result = pokey_dev->read(*machine().firstcpu->space(), offset & 0x0f, 0xff); + } /* IN0 */ else if (offset < 0x4900) @@ -1046,7 +1053,7 @@ static MACHINE_CONFIG_START( missile, missile_state ) /* sound hardware */ MCFG_SPEAKER_STANDARD_MONO("mono") - MCFG_SOUND_ADD("pokey", POKEY, MASTER_CLOCK/8) + MCFG_SOUND_ADD("pokey", POKEYN, MASTER_CLOCK/8) MCFG_SOUND_CONFIG(pokey_config) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0) MACHINE_CONFIG_END