diff options
Diffstat (limited to 'src/mame/machine/balsente.c')
-rw-r--r-- | src/mame/machine/balsente.c | 1251 |
1 files changed, 1251 insertions, 0 deletions
diff --git a/src/mame/machine/balsente.c b/src/mame/machine/balsente.c new file mode 100644 index 00000000000..cfe4ec0332d --- /dev/null +++ b/src/mame/machine/balsente.c @@ -0,0 +1,1251 @@ +/*************************************************************************** + + Bally/Sente SAC-1 system + + driver by Aaron Giles + +***************************************************************************/ + +#include "driver.h" +#include "cpu/m6809/m6809.h" +#include "balsente.h" +#include "sound/cem3394.h" +#include <math.h> + + +#define LOG_CEM_WRITES 0 + + +/* local prototypes */ +static void poly17_init(void); +static void counter_set_out(int which, int gate); +static TIMER_CALLBACK( counter_callback ); +static TIMER_CALLBACK( clock_counter_0_ff ); +static void update_grudge_steering(void); + +/* global data */ +UINT8 balsente_shooter; +UINT8 balsente_shooter_x; +UINT8 balsente_shooter_y; +UINT8 balsente_adc_shift; +UINT16 *shrike_shared; +UINT16 *shrike_io; + + +/* 8253 counter state */ +struct counter_state +{ + emu_timer *timer; + UINT8 timer_active; + INT32 initial; + INT32 count; + UINT8 gate; + UINT8 out; + UINT8 mode; + UINT8 readbyte; + UINT8 writebyte; +}; + +static struct counter_state counter[3]; + +static emu_timer *scanline_timer; + +/* manually clocked counter 0 states */ +static UINT8 counter_control; +static UINT8 counter_0_ff; +static emu_timer *counter_0_timer; +static UINT8 counter_0_timer_active; + +/* random number generator states */ +static UINT8 *poly17 = NULL; +static UINT8 *rand17 = NULL; + +/* ADC I/O states */ +static INT8 analog_input_data[4]; +static UINT8 adc_value; + +/* CEM3394 DAC control states */ +static UINT16 dac_value; +static UINT8 dac_register; +static UINT8 chip_select; + +/* main CPU 6850 states */ +static UINT8 m6850_status; +static UINT8 m6850_control; +static UINT8 m6850_input; +static UINT8 m6850_output; +static UINT8 m6850_data_ready; + +/* sound CPU 6850 states */ +static UINT8 m6850_sound_status; +static UINT8 m6850_sound_control; +static UINT8 m6850_sound_input; +static UINT8 m6850_sound_output; + +/* noise generator states */ +static UINT32 noise_position[6]; + +/* game-specific states */ +static UINT8 nstocker_bits; +static UINT8 spiker_expand_color; +static UINT8 spiker_expand_bgcolor; +static UINT8 spiker_expand_bits; +static UINT8 grudge_steering_result; +static UINT8 grudge_last_steering[3]; + + + +/************************************* + * + * Interrupt handling + * + *************************************/ + +static TIMER_CALLBACK( irq_off ) +{ + cpunum_set_input_line(0, M6809_IRQ_LINE, CLEAR_LINE); +} + + +static TIMER_CALLBACK( interrupt_timer ) +{ + /* next interrupt after scanline 256 is scanline 64 */ + if (param == 256) + timer_adjust(scanline_timer, video_screen_get_time_until_pos(0, 64, 0), 64, attotime_zero); + else + timer_adjust(scanline_timer, video_screen_get_time_until_pos(0, param + 64, 0), param + 64, attotime_zero); + + /* IRQ starts on scanline 0, 64, 128, etc. */ + cpunum_set_input_line(0, M6809_IRQ_LINE, ASSERT_LINE); + + /* it will turn off on the next HBLANK */ + timer_set(video_screen_get_time_until_pos(0, param, BALSENTE_HBSTART), 0, irq_off); + + /* if this is Grudge Match, update the steering */ + if (grudge_steering_result & 0x80) + update_grudge_steering(); + + /* if we're a shooter, we do a little more work */ + if (balsente_shooter) + { + UINT8 tempx, tempy; + + /* we latch the beam values on the first interrupt after VBLANK */ + if (param == 64 && balsente_shooter) + { + balsente_shooter_x = readinputport(8); + balsente_shooter_y = readinputport(9); + } + + /* which bits get returned depends on which scanline we're at */ + tempx = balsente_shooter_x << ((param - 64) / 64); + tempy = balsente_shooter_y << ((param - 64) / 64); + nstocker_bits = ((tempx >> 4) & 0x08) | ((tempx >> 1) & 0x04) | + ((tempy >> 6) & 0x02) | ((tempy >> 3) & 0x01); + } +} + + +MACHINE_RESET( balsente ) +{ + int numbanks, i; + + /* create the polynomial tables */ + poly17_init(); + + /* reset counters; counter 2's gate is tied high */ + memset(counter, 0, sizeof(counter)); + counter[1].timer = timer_alloc(counter_callback); + counter[2].timer = timer_alloc(counter_callback); + counter[2].gate = 1; + + /* reset the manual counter 0 clock */ + counter_control = 0x00; + counter_0_ff = 0; + counter_0_timer = timer_alloc(clock_counter_0_ff); + counter_0_timer_active = 0; + + /* reset the ADC states */ + adc_value = 0; + + /* reset the CEM3394 I/O states */ + dac_value = 0; + dac_register = 0; + chip_select = 0x3f; + + /* reset game-specific states */ + grudge_steering_result = 0; + + /* reset the 6850 chips */ + balsente_m6850_w(0, 3); + balsente_m6850_sound_w(0, 3); + + /* reset the noise generator */ + memset(noise_position, 0, sizeof(noise_position)); + + /* point the banks to bank 0 */ + numbanks = (memory_region_length(REGION_CPU1) > 0x40000) ? 16 : 8; + memory_configure_bank(1, 0, numbanks, &memory_region(REGION_CPU1)[0x10000], 0x6000); + memory_configure_bank(2, 0, numbanks, &memory_region(REGION_CPU1)[0x12000], 0x6000); + memory_set_bank(1, 0); + memory_set_bank(2, 0); + + /* start a timer to generate interrupts */ + scanline_timer = timer_alloc(interrupt_timer); + timer_adjust(scanline_timer, video_screen_get_time_until_pos(0, 0, 0), 0, attotime_zero); + + /* register for saving */ + for (i = 0; i < 3; i++) + { + state_save_register_item("8253counter", i, counter[i].timer_active); + state_save_register_item("8253counter", i, counter[i].initial); + state_save_register_item("8253counter", i, counter[i].count); + state_save_register_item("8253counter", i, counter[i].gate); + state_save_register_item("8253counter", i, counter[i].out); + state_save_register_item("8253counter", i, counter[i].mode); + state_save_register_item("8253counter", i, counter[i].readbyte); + state_save_register_item("8253counter", i, counter[i].writebyte); + } + + state_save_register_global(counter_control); + state_save_register_global(counter_0_ff); + state_save_register_global(counter_0_timer_active); + + state_save_register_global_array(analog_input_data); + state_save_register_global(adc_value); + + state_save_register_global(dac_value); + state_save_register_global(dac_register); + state_save_register_global(chip_select); + + state_save_register_global(m6850_status); + state_save_register_global(m6850_control); + state_save_register_global(m6850_input); + state_save_register_global(m6850_output); + state_save_register_global(m6850_data_ready); + + state_save_register_global(m6850_sound_status); + state_save_register_global(m6850_sound_control); + state_save_register_global(m6850_sound_input); + state_save_register_global(m6850_sound_output); + + state_save_register_global_array(noise_position); + + state_save_register_global(nstocker_bits); + state_save_register_global(spiker_expand_color); + state_save_register_global(spiker_expand_bgcolor); + state_save_register_global(spiker_expand_bits); + state_save_register_global(grudge_steering_result); + state_save_register_global_array(grudge_last_steering); +} + + + +/************************************* + * + * MM5837 noise generator + * + * NOTE: this is stolen straight from + * POKEY.c + * + *************************************/ + +#define POLY17_BITS 17 +#define POLY17_SIZE ((1 << POLY17_BITS) - 1) +#define POLY17_SHL 7 +#define POLY17_SHR 10 +#define POLY17_ADD 0x18000 + +static void poly17_init(void) +{ + UINT32 i, x = 0; + UINT8 *p, *r; + + /* allocate memory */ + p = poly17 = auto_malloc(POLY17_SIZE + 1); + r = rand17 = auto_malloc(POLY17_SIZE + 1); + + /* generate the polynomial */ + for (i = 0; i < POLY17_SIZE; i++) + { + /* store new values */ + *p++ = x & 1; + *r++ = x >> 3; + + /* calculate next bit */ + x = ((x << POLY17_SHL) + (x >> POLY17_SHR) + POLY17_ADD) & POLY17_SIZE; + } +} + + +void balsente_noise_gen(int chip, int count, short *buffer) +{ + /* noise generator runs at 100kHz */ + UINT32 step = (100000 << 14) / CEM3394_SAMPLE_RATE; + UINT32 noise_counter = noise_position[chip]; + + while (count--) + { + *buffer++ = poly17[(noise_counter >> 14) & POLY17_SIZE] << 12; + noise_counter += step; + } + + /* remember the noise position */ + noise_position[chip] = noise_counter; +} + + + +/************************************* + * + * Hardware random numbers + * + *************************************/ + +WRITE8_HANDLER( balsente_random_reset_w ) +{ + /* reset random number generator */ +} + + +READ8_HANDLER( balsente_random_num_r ) +{ + UINT32 cc; + + /* CPU runs at 1.25MHz, noise source at 100kHz --> multiply by 12.5 */ + cc = activecpu_gettotalcycles(); + + /* 12.5 = 8 + 4 + 0.5 */ + cc = (cc << 3) + (cc << 2) + (cc >> 1); + return rand17[cc & POLY17_SIZE]; +} + + + +/************************************* + * + * ROM banking + * + *************************************/ + +WRITE8_HANDLER( balsente_rombank_select_w ) +{ + /* the bank number comes from bits 4-6 */ + memory_set_bank(1, (data >> 4) & 7); + memory_set_bank(2, (data >> 4) & 7); +} + + +WRITE8_HANDLER( balsente_rombank2_select_w ) +{ + /* Night Stocker and Name that Tune only so far.... */ + int bank = data & 7; + + /* top bit controls which half of the ROMs to use (Name that Tune only) */ + if (memory_region_length(REGION_CPU1) > 0x40000) bank |= (data >> 4) & 8; + + /* when they set the AB bank, it appears as though the CD bank is reset */ + if (data & 0x20) + { + memory_set_bank(1, bank); + memory_set_bank(2, 6); + } + + /* set both banks */ + else + { + memory_set_bank(1, bank); + memory_set_bank(2, bank); + } +} + + + +/************************************* + * + * Special outputs + * + *************************************/ + +WRITE8_HANDLER( balsente_misc_output_w ) +{ + offset = (offset / 4) % 8; + data >>= 7; + + /* these are generally used to control the various lamps */ + /* special case is offset 7, which recalls the NVRAM data */ + if (offset == 7) + { + logerror("nvrecall_w=%d\n", data); + } + else + { +// set_led_status(offset, data); + } +} + + + +/************************************* + * + * 6850 UART communications + * + *************************************/ + +static void m6850_update_io(void) +{ + UINT8 new_state; + + /* sound -> main CPU communications */ + if (!(m6850_sound_status & 0x02)) + { + /* set the overrun bit if the data in the destination hasn't been read yet */ + if (m6850_status & 0x01) + m6850_status |= 0x20; + + /* copy the sound's output to our input */ + m6850_input = m6850_sound_output; + + /* set the receive register full bit */ + m6850_status |= 0x01; + + /* set the sound's trasmitter register empty bit */ + m6850_sound_status |= 0x02; + } + + /* main -> sound CPU communications */ + if (m6850_data_ready) + { + /* set the overrun bit if the data in the destination hasn't been read yet */ + if (m6850_sound_status & 0x01) + m6850_sound_status |= 0x20; + + /* copy the main CPU's output to our input */ + m6850_sound_input = m6850_output; + + /* set the receive register full bit */ + m6850_sound_status |= 0x01; + + /* set the main CPU's trasmitter register empty bit */ + m6850_status |= 0x02; + m6850_data_ready = 0; + } + + /* check for reset states */ + if ((m6850_control & 3) == 3) + { + m6850_status = 0x02; + m6850_data_ready = 0; + } + if ((m6850_sound_control & 3) == 3) + m6850_sound_status = 0x02; + + /* check for transmit/receive IRQs on the main CPU */ + new_state = 0; + if ((m6850_control & 0x80) && (m6850_status & 0x21)) new_state = 1; + if ((m6850_control & 0x60) == 0x20 && (m6850_status & 0x02)) new_state = 1; + + /* apply the change */ + if (new_state && !(m6850_status & 0x80)) + { + cpunum_set_input_line(0, M6809_FIRQ_LINE, ASSERT_LINE); + m6850_status |= 0x80; + } + else if (!new_state && (m6850_status & 0x80)) + { + cpunum_set_input_line(0, M6809_FIRQ_LINE, CLEAR_LINE); + m6850_status &= ~0x80; + } + + /* check for transmit/receive IRQs on the sound CPU */ + new_state = 0; + if ((m6850_sound_control & 0x80) && (m6850_sound_status & 0x21)) new_state = 1; + if ((m6850_sound_control & 0x60) == 0x20 && (m6850_sound_status & 0x02)) new_state = 1; + if (!(counter_control & 0x20)) new_state = 0; + + /* apply the change */ + if (new_state && !(m6850_sound_status & 0x80)) + { + cpunum_set_input_line(1, INPUT_LINE_NMI, ASSERT_LINE); + m6850_sound_status |= 0x80; + } + else if (!new_state && (m6850_sound_status & 0x80)) + { + cpunum_set_input_line(1, INPUT_LINE_NMI, CLEAR_LINE); + m6850_sound_status &= ~0x80; + } +} + + + +/************************************* + * + * 6850 UART (main CPU) + * + *************************************/ + +READ8_HANDLER( balsente_m6850_r ) +{ + int result; + + /* status register is at offset 0 */ + if (offset == 0) + { + result = m6850_status; + } + + /* input register is at offset 1 */ + else + { + result = m6850_input; + + /* clear the overrun and receive buffer full bits */ + m6850_status &= ~0x21; + m6850_update_io(); + } + + return result; +} + + +static TIMER_CALLBACK( m6850_data_ready_callback ) +{ + /* set the output data byte and indicate that we're ready to go */ + m6850_output = param; + m6850_data_ready = 1; + m6850_update_io(); +} + + +static TIMER_CALLBACK( m6850_w_callback ) +{ + /* indicate that the transmit buffer is no longer empty and update the I/O state */ + m6850_status &= ~0x02; + m6850_update_io(); + + /* set a timer for 500usec later to actually transmit the data */ + /* (this is very important for several games, esp Snacks'n Jaxson) */ + timer_set(ATTOTIME_IN_USEC(500), param, m6850_data_ready_callback); +} + + +WRITE8_HANDLER( balsente_m6850_w ) +{ + /* control register is at offset 0 */ + if (offset == 0) + { + m6850_control = data; + + /* re-update since interrupt enables could have been modified */ + m6850_update_io(); + } + + /* output register is at offset 1; set a timer to synchronize the CPUs */ + else + timer_call_after_resynch(data, m6850_w_callback); +} + + + +/************************************* + * + * 6850 UART (sound CPU) + * + *************************************/ + +READ8_HANDLER( balsente_m6850_sound_r ) +{ + int result; + + /* status register is at offset 0 */ + if (offset == 0) + { + result = m6850_sound_status; + } + + /* input register is at offset 1 */ + else + { + result = m6850_sound_input; + + /* clear the overrun and receive buffer full bits */ + m6850_sound_status &= ~0x21; + m6850_update_io(); + } + + return result; +} + + +WRITE8_HANDLER( balsente_m6850_sound_w ) +{ + /* control register is at offset 0 */ + if (offset == 0) + m6850_sound_control = data; + + /* output register is at offset 1 */ + else + { + m6850_sound_output = data; + m6850_sound_status &= ~0x02; + } + + /* re-update since interrupt enables could have been modified */ + m6850_update_io(); +} + + + +/************************************* + * + * ADC handlers + * + *************************************/ + +INTERRUPT_GEN( balsente_update_analog_inputs ) +{ + int i; + + /* the analog input system helpfully scales the value read by the percentage of time */ + /* into the current frame we are; unfortunately, this is bad for us, since the analog */ + /* ports are read once a frame, just at varying intervals. To get around this, we */ + /* read all the analog inputs at VBLANK time and just return the cached values. */ + for (i = 0; i < 4; i++) + analog_input_data[i] = readinputport(4 + i); +} + + +static TIMER_CALLBACK( adc_finished ) +{ + int which = param; + + /* analog controls are read in two pieces; the lower port returns the sign */ + /* and the upper port returns the absolute value of the magnitude */ + int val = analog_input_data[which / 2] << balsente_adc_shift; + + /* special case for Stompin'/Shrike Avenger */ + if (balsente_adc_shift == 32) + { + adc_value = analog_input_data[which]; + return; + } + + /* push everything out a little bit extra; most games seem to have a dead */ + /* zone in the middle that feels unnatural with the mouse */ + if (val < 0) val -= 8; + else if (val > 0) val += 8; + + /* clip to 0xff maximum magnitude */ + if (val < -0xff) val = -0xff; + else if (val > 0xff) val = 0xff; + + /* return the sign */ + if (!(which & 1)) + adc_value = (val < 0) ? 0xff : 0x00; + + /* return the magnitude */ + else + adc_value = (val < 0) ? -val : val; +} + + +READ8_HANDLER( balsente_adc_data_r ) +{ + /* just return the last value read */ + return adc_value; +} + + +WRITE8_HANDLER( balsente_adc_select_w ) +{ + /* set a timer to go off and read the value after 50us */ + /* it's important that we do this for Mini Golf */ +logerror("adc_select %d\n", offset & 7); + timer_set(ATTOTIME_IN_USEC(50), offset & 7, adc_finished); +} + + + +/************************************* + * + * 8253-5 timer utilities + * + * NOTE: this is far from complete! + * + *************************************/ + +INLINE void counter_start(int which) +{ + /* don't start a timer for channel 0; it is clocked manually */ + if (which != 0) + { + /* only start a timer if we're gated and there is none already */ + if (counter[which].gate && !counter[which].timer_active) + { + counter[which].timer_active = 1; + timer_adjust(counter[which].timer, attotime_mul(ATTOTIME_IN_HZ(2000000), counter[which].count), which, attotime_zero); + } + } +} + + +INLINE void counter_stop(int which) +{ + /* only stop the timer if it exists */ + if (counter[which].timer_active) + timer_adjust(counter[which].timer, attotime_never, 0, attotime_zero); + counter[which].timer_active = 0; +} + + +INLINE void counter_update_count(int which) +{ + /* only update if the timer is running */ + if (counter[which].timer_active) + { + /* determine how many 2MHz cycles are remaining */ + int count = attotime_to_double(attotime_mul(timer_timeleft(counter[which].timer), 2000000)); + counter[which].count = (count < 0) ? 0 : count; + } +} + + + +/************************************* + * + * 8253-5 timer internals + * + * NOTE: this is far from complete! + * + *************************************/ + +static void counter_set_gate(int which, int gate) +{ + int oldgate = counter[which].gate; + + /* remember the gate state */ + counter[which].gate = gate; + + /* if the counter is being halted, update the count and remove the system timer */ + if (!gate && oldgate) + { + counter_update_count(which); + counter_stop(which); + } + + /* if the counter is being started, create the timer */ + else if (gate && !oldgate) + { + /* mode 1 waits for the gate to trigger the counter */ + if (counter[which].mode == 1) + { + counter_set_out(which, 0); + + /* add one to the count; technically, OUT goes low on the next clock pulse */ + /* and then starts counting down; it's important that we don't count the first one */ + counter[which].count = counter[which].initial + 1; + } + + /* start the counter */ + counter_start(which); + } +} + + +static void counter_set_out(int which, int out) +{ + /* OUT on counter 2 is hooked to the /INT line on the Z80 */ + if (which == 2) + cpunum_set_input_line(1, 0, out ? ASSERT_LINE : CLEAR_LINE); + + /* OUT on counter 0 is hooked to the GATE line on counter 1 */ + else if (which == 0) + counter_set_gate(1, !out); + + /* remember the out state */ + counter[which].out = out; +} + + +static TIMER_CALLBACK( counter_callback ) +{ + /* reset the counter and the count */ + counter[param].timer_active = 0; + counter[param].count = 0; + + /* set the state of the OUT line */ + /* mode 0 and 1: when firing, transition OUT to high */ + if (counter[param].mode == 0 || counter[param].mode == 1) + counter_set_out(param, 1); + + /* no other modes handled currently */ +} + + + +/************************************* + * + * 8253-5 timer handlers + * + * NOTE: this is far from complete! + * + *************************************/ + +READ8_HANDLER( balsente_counter_8253_r ) +{ + int which; + + switch (offset & 3) + { + case 0: + case 1: + case 2: + /* warning: assumes LSB/MSB addressing and no latching! */ + which = offset & 3; + + /* update the count */ + counter_update_count(which); + + /* return the LSB */ + if (counter[which].readbyte == 0) + { + counter[which].readbyte = 1; + return counter[which].count & 0xff; + } + + /* write the MSB and reset the counter */ + else + { + counter[which].readbyte = 0; + return (counter[which].count >> 8) & 0xff; + } + break; + } + return 0; +} + + +WRITE8_HANDLER( balsente_counter_8253_w ) +{ + int which; + + switch (offset & 3) + { + case 0: + case 1: + case 2: + /* warning: assumes LSB/MSB addressing and no latching! */ + which = offset & 3; + + /* if the counter is in mode 0, a write here will reset the OUT state */ + if (counter[which].mode == 0) + counter_set_out(which, 0); + + /* write the LSB */ + if (counter[which].writebyte == 0) + { + counter[which].count = (counter[which].count & 0xff00) | (data & 0x00ff); + counter[which].initial = (counter[which].initial & 0xff00) | (data & 0x00ff); + counter[which].writebyte = 1; + } + + /* write the MSB and reset the counter */ + else + { + counter[which].count = (counter[which].count & 0x00ff) | ((data << 8) & 0xff00); + counter[which].initial = (counter[which].initial & 0x00ff) | ((data << 8) & 0xff00); + counter[which].writebyte = 0; + + /* treat 0 as $10000 */ + if (counter[which].count == 0) counter[which].count = counter[which].initial = 0x10000; + + /* remove any old timer and set a new one */ + counter_stop(which); + + /* note that in mode 1, we have to wait for a rising edge of a gate */ + if (counter[which].mode == 0) + counter_start(which); + + /* if the counter is in mode 1, a write here will set the OUT state */ + if (counter[which].mode == 1) + counter_set_out(which, 1); + } + break; + + case 3: + /* determine which counter */ + which = data >> 6; + if (which == 3) break; + + /* if the counter was in mode 0, a write here will reset the OUT state */ + if (((counter[which].mode >> 1) & 7) == 0) + counter_set_out(which, 0); + + /* set the mode */ + counter[which].mode = (data >> 1) & 7; + + /* if the counter is in mode 0, a write here will reset the OUT state */ + if (counter[which].mode == 0) + counter_set_out(which, 0); + break; + } +} + + + +/************************************* + * + * Sound CPU counter 0 emulation + * + *************************************/ + +static void set_counter_0_ff(running_machine *machine, int newstate) +{ + /* the flip/flop output is inverted, so if we went high to low, that's a clock */ + if (counter_0_ff && !newstate) + { + /* only count if gated and non-zero */ + if (counter[0].count > 0 && counter[0].gate) + { + counter[0].count--; + if (counter[0].count == 0) + counter_callback(machine, 0); + } + } + + /* remember the new state */ + counter_0_ff = newstate; +} + + +static TIMER_CALLBACK( clock_counter_0_ff ) +{ + /* clock the D value through the flip-flop */ + set_counter_0_ff(machine, (counter_control >> 3) & 1); +} + + +static void update_counter_0_timer(void) +{ + double maxfreq = 0.0; + int i; + + /* if there's already a timer, remove it */ + if (counter_0_timer_active) + timer_adjust(counter_0_timer, attotime_never, 0, attotime_zero); + counter_0_timer_active = 0; + + /* find the counter with the maximum frequency */ + /* this is used to calibrate the timers at startup */ + for (i = 0; i < 6; i++) + if (cem3394_get_parameter(i, CEM3394_FINAL_GAIN) < 10.0) + { + double tempfreq; + + /* if the filter resonance is high, then they're calibrating the filter frequency */ + if (cem3394_get_parameter(i, CEM3394_FILTER_RESONANCE) > 0.9) + tempfreq = cem3394_get_parameter(i, CEM3394_FILTER_FREQENCY); + + /* otherwise, they're calibrating the VCO frequency */ + else + tempfreq = cem3394_get_parameter(i, CEM3394_VCO_FREQUENCY); + + if (tempfreq > maxfreq) maxfreq = tempfreq; + } + + /* reprime the timer */ + if (maxfreq > 0.0) + { + counter_0_timer_active = 1; + timer_adjust(counter_0_timer, ATTOTIME_IN_HZ(maxfreq), 0, ATTOTIME_IN_HZ(maxfreq)); + } +} + + + +/************************************* + * + * Sound CPU counter handlers + * + *************************************/ + +READ8_HANDLER( balsente_counter_state_r ) +{ + /* bit D0 is the inverse of the flip-flop state */ + int result = !counter_0_ff; + + /* bit D1 is the OUT value from counter 0 */ + if (counter[0].out) result |= 0x02; + + return result; +} + + +WRITE8_HANDLER( balsente_counter_control_w ) +{ + UINT8 diff_counter_control = counter_control ^ data; + + /* set the new global value */ + counter_control = data; + + /* bit D0 enables/disables audio */ + if (diff_counter_control & 0x01) + { + int ch; + for (ch = 0; ch < 6; ch++) + sndti_set_output_gain(SOUND_CEM3394, ch, 0, (data & 0x01) ? 1.0 : 0); + } + + /* bit D1 is hooked to counter 0's gate */ + /* if we gate on, start a pulsing timer to clock it */ + if (!counter[0].gate && (data & 0x02) && !counter_0_timer_active) + { + update_counter_0_timer(); + } + + /* if we gate off, remove the timer */ + else if (counter[0].gate && !(data & 0x02) && counter_0_timer_active) + { + timer_adjust(counter_0_timer, attotime_never, 0, attotime_zero); + counter_0_timer_active = 0; + } + + /* set the actual gate afterwards, since we need to know the old value above */ + counter_set_gate(0, (data >> 1) & 1); + + /* bits D2 and D4 control the clear/reset flags on the flip-flop that feeds counter 0 */ + if (!(data & 0x04)) set_counter_0_ff(Machine, 1); + if (!(data & 0x10)) set_counter_0_ff(Machine, 0); + + /* bit 5 clears the NMI interrupt; recompute the I/O state now */ + m6850_update_io(); +} + + + +/************************************* + * + * CEM3394 Interfaces + * + *************************************/ + +WRITE8_HANDLER( balsente_chip_select_w ) +{ + static const UINT8 register_map[8] = + { + CEM3394_VCO_FREQUENCY, + CEM3394_FINAL_GAIN, + CEM3394_FILTER_RESONANCE, + CEM3394_FILTER_FREQENCY, + CEM3394_MIXER_BALANCE, + CEM3394_MODULATION_AMOUNT, + CEM3394_PULSE_WIDTH, + CEM3394_WAVE_SELECT + }; + + double voltage = (double)dac_value * (8.0 / 4096.0) - 4.0; + int diffchip = data ^ chip_select, i; + int reg = register_map[dac_register]; + + /* remember the new select value */ + chip_select = data; + + /* check all six chip enables */ + for (i = 0; i < 6; i++) + if ((diffchip & (1 << i)) && (data & (1 << i))) + { + double temp = 0; + + /* remember the previous value */ + temp = cem3394_get_parameter(i, reg); + + /* set the voltage */ + cem3394_set_voltage(i, reg, voltage); + + /* only log changes */ +#if LOG_CEM_WRITES + if (temp != cem3394_get_parameter(i, reg)) + { + static const char *names[] = + { + "VCO_FREQUENCY", + "FINAL_GAIN", + "FILTER_RESONANCE", + "FILTER_FREQENCY", + "MIXER_BALANCE", + "MODULATION_AMOUNT", + "PULSE_WIDTH", + "WAVE_SELECT" + }; + logerror("s%04X: CEM#%d:%s=%f\n", activecpu_get_previouspc(), i, names[dac_register], voltage); + } +#endif + } + + /* if a timer for counter 0 is running, recompute */ + if (counter_0_timer_active) + update_counter_0_timer(); +} + + + +WRITE8_HANDLER( balsente_dac_data_w ) +{ + /* LSB or MSB? */ + if (offset & 1) + dac_value = (dac_value & 0xfc0) | ((data >> 2) & 0x03f); + else + dac_value = (dac_value & 0x03f) | ((data << 6) & 0xfc0); + + /* if there are open channels, force the values in */ + if ((chip_select & 0x3f) != 0x3f) + { + UINT8 temp = chip_select; + balsente_chip_select_w(0, 0x3f); + balsente_chip_select_w(0, temp); + } +} + + +WRITE8_HANDLER( balsente_register_addr_w ) +{ + dac_register = data & 7; +} + + + +/************************************* + * + * Game-specific handlers + * + *************************************/ + +READ8_HANDLER( nstocker_port2_r ) +{ + return (readinputport(2) & 0xf0) | nstocker_bits; +} + + +WRITE8_HANDLER( spiker_expand_w ) +{ + /* offset 0 is the bit pattern */ + if (offset == 0) + spiker_expand_bits = data; + + /* offset 1 is the background color (cleared on each read) */ + else if (offset == 1) + spiker_expand_bgcolor = data; + + /* offset 2 is the color */ + else if (offset == 2) + spiker_expand_color = data; +} + + +READ8_HANDLER( spiker_expand_r ) +{ + UINT8 left, right; + + /* first rotate each nibble */ + spiker_expand_bits = ((spiker_expand_bits << 1) & 0xee) | ((spiker_expand_bits >> 3) & 0x11); + + /* compute left and right pixels */ + left = (spiker_expand_bits & 0x10) ? spiker_expand_color : spiker_expand_bgcolor; + right = (spiker_expand_bits & 0x01) ? spiker_expand_color : spiker_expand_bgcolor; + + /* reset the background color */ + spiker_expand_bgcolor = 0; + + /* return the combined result */ + return (left & 0xf0) | (right & 0x0f); +} + + +static void update_grudge_steering(void) +{ + UINT8 wheel[3]; + INT8 diff[3]; + + /* read the current steering values */ + wheel[0] = readinputport(4); + wheel[1] = readinputport(5); + wheel[2] = readinputport(6); + + /* diff the values */ + diff[0] = wheel[0] - grudge_last_steering[0]; + diff[1] = wheel[1] - grudge_last_steering[1]; + diff[2] = wheel[2] - grudge_last_steering[2]; + + /* update the last values */ + grudge_last_steering[0] += diff[0]; + grudge_last_steering[1] += diff[1]; + grudge_last_steering[2] += diff[2]; + + /* compute the result */ + grudge_steering_result = 0xff; + if (diff[0]) + { + grudge_steering_result ^= 0x01; + if (diff[0] > 0) grudge_steering_result ^= 0x02; + } + if (diff[1]) + { + grudge_steering_result ^= 0x04; + if (diff[1] > 0) grudge_steering_result ^= 0x08; + } + if (diff[2]) + { + grudge_steering_result ^= 0x10; + if (diff[2] > 0) grudge_steering_result ^= 0x20; + } + logerror("Recomputed steering\n"); +} + + +READ8_HANDLER( grudge_steering_r ) +{ + logerror("%04X:grudge_steering_r(@%d)\n", activecpu_get_pc(), video_screen_get_vpos(0)); + grudge_steering_result |= 0x80; + return grudge_steering_result; +} + + + +/************************************* + * + * Shrike Avenger CPU memory handlers + * + *************************************/ + +READ8_HANDLER( shrike_shared_6809_r ) +{ + UINT16 mem_mask = offset & 1 ? 0xff : 0xff00; + + switch( offset ) + { + case 6: // return OK for 68k status register until motors hooked up + return 0; + default: + return ( shrike_shared[offset >> 1] & mem_mask ) >> ( ~mem_mask & 8 ); + } +} + + +WRITE8_HANDLER( shrike_shared_6809_w ) +{ + UINT16 mem_mask = offset & 1 ? 0xff : 0xff00; + shrike_shared[offset >> 1] = ( shrike_shared[offset >> 1] & ~mem_mask ) | ( data << ( ~mem_mask & 0x8 ) ); +} + +// uses movep, so writes even 8 bit addresses to odd 16 bit addresses, reads as 16 bit from odd addresses +// i.e. write 0xdeadbeef to 10000, read 0xde from 10001, 0xad from 10003, 0xbe from 10005... +WRITE16_HANDLER( shrike_io_68k_w ) +{ + COMBINE_DATA( &shrike_io[offset] ); +} + +READ16_HANDLER( shrike_io_68k_r ) +{ + return ( shrike_io[offset] & ~mem_mask ) >> ( 8 & mem_mask ); +} + |