diff options
Diffstat (limited to 'src/mame/machine/atarigen.c')
-rw-r--r-- | src/mame/machine/atarigen.c | 1500 |
1 files changed, 1500 insertions, 0 deletions
diff --git a/src/mame/machine/atarigen.c b/src/mame/machine/atarigen.c new file mode 100644 index 00000000000..ddff7ce6da5 --- /dev/null +++ b/src/mame/machine/atarigen.c @@ -0,0 +1,1500 @@ +/*########################################################################## + + atarigen.c + + General functions for Atari raster games. + +##########################################################################*/ + + +#include "driver.h" +#include "atarigen.h" +#include "slapstic.h" +#include "cpu/m6502/m6502.h" + + + +/*########################################################################## + CONSTANTS +##########################################################################*/ + +#define SOUND_TIMER_RATE ATTOTIME_IN_USEC(5) +#define SOUND_TIMER_BOOST ATTOTIME_IN_USEC(100) + + + +/*########################################################################## + GLOBAL VARIABLES +##########################################################################*/ + +UINT8 atarigen_scanline_int_state; +UINT8 atarigen_sound_int_state; +UINT8 atarigen_video_int_state; + +const UINT16 * atarigen_eeprom_default; +UINT16 * atarigen_eeprom; +size_t atarigen_eeprom_size; + +UINT8 atarigen_cpu_to_sound_ready; +UINT8 atarigen_sound_to_cpu_ready; + +UINT16 * atarigen_playfield; +UINT16 * atarigen_playfield2; +UINT16 * atarigen_playfield_upper; +UINT16 * atarigen_alpha; +UINT16 * atarigen_alpha2; +UINT16 * atarigen_xscroll; +UINT16 * atarigen_yscroll; + +UINT32 * atarigen_playfield32; +UINT32 * atarigen_alpha32; + +tilemap * atarigen_playfield_tilemap; +tilemap * atarigen_playfield2_tilemap; +tilemap * atarigen_alpha_tilemap; +tilemap * atarigen_alpha2_tilemap; + +UINT16 * atarivc_data; +UINT16 * atarivc_eof_data; +struct atarivc_state_desc atarivc_state; + + + +/*########################################################################## + STATIC VARIABLES +##########################################################################*/ + +static atarigen_int_callback update_int_callback; +static emu_timer * scanline_interrupt_timer; + +static UINT8 eeprom_unlocked; + +static UINT8 atarigen_slapstic_num; +static UINT16 * atarigen_slapstic; +static UINT32 atarigen_slapstic_bank; +static void * atarigen_slapstic_bank0; + +static UINT8 sound_cpu_num; +static UINT8 atarigen_cpu_to_sound; +static UINT8 atarigen_sound_to_cpu; +static UINT8 timed_int; +static UINT8 ym2151_int; + +static atarigen_scanline_callback scanline_callback; +static UINT32 scanlines_per_callback; + +static UINT32 actual_vc_latch0; +static UINT32 actual_vc_latch1; +static UINT8 atarivc_playfields; + +static UINT32 playfield_latch; +static UINT32 playfield2_latch; + +static emu_timer * scanline_timer[ATARIMO_MAX]; +static emu_timer * atarivc_eof_update_timer[ATARIMO_MAX]; + + + +/*########################################################################## + STATIC FUNCTION DECLARATIONS +##########################################################################*/ + +static TIMER_CALLBACK( scanline_interrupt_callback ); + +static void decompress_eeprom_word(const UINT16 *data); +static void decompress_eeprom_byte(const UINT16 *data); + +static void update_6502_irq(void); +static TIMER_CALLBACK( delayed_sound_reset ); +static TIMER_CALLBACK( delayed_sound_w ); +static TIMER_CALLBACK( delayed_6502_sound_w ); + +static void atarigen_set_vol(running_machine *machine, int volume, sound_type type); + +static TIMER_CALLBACK( scanline_timer_callback ); + +static void atarivc_common_w(int scrnum, offs_t offset, UINT16 newword); + +static TIMER_CALLBACK( unhalt_cpu ); + +static TIMER_CALLBACK( atarivc_eof_update ); + + + +/*########################################################################## + INTERRUPT HANDLING +##########################################################################*/ + +/*--------------------------------------------------------------- + atarigen_interrupt_reset: Initializes the state of all + the interrupt sources. +---------------------------------------------------------------*/ + +void atarigen_interrupt_reset(atarigen_int_callback update_int) +{ + int i; + + /* set the callback */ + update_int_callback = update_int; + + /* reset the interrupt states */ + atarigen_video_int_state = atarigen_sound_int_state = atarigen_scanline_int_state = 0; + scanline_interrupt_timer = NULL; + + /* create timers */ + for (i = 0; i < ATARIMO_MAX; i++) + scanline_timer[i] = timer_alloc(scanline_timer_callback); + + /* create a timer for scanlines */ + scanline_interrupt_timer = timer_alloc(scanline_interrupt_callback); +} + + +/*--------------------------------------------------------------- + atarigen_update_interrupts: Forces the interrupt callback + to be called with the current VBLANK and sound interrupt + states. +---------------------------------------------------------------*/ + +void atarigen_update_interrupts(void) +{ + (*update_int_callback)(); +} + + + +/*--------------------------------------------------------------- + atarigen_scanline_int_set: Sets the scanline when the next + scanline interrupt should be generated. +---------------------------------------------------------------*/ + +void atarigen_scanline_int_set(int scrnum, int scanline) +{ + timer_adjust(scanline_interrupt_timer, video_screen_get_time_until_pos(scrnum, scanline, 0), scrnum, attotime_zero); +} + + +/*--------------------------------------------------------------- + atarigen_scanline_int_gen: Standard interrupt routine + which sets the scanline interrupt state. +---------------------------------------------------------------*/ + +INTERRUPT_GEN( atarigen_scanline_int_gen ) +{ + atarigen_scanline_int_state = 1; + (*update_int_callback)(); +} + + +/*--------------------------------------------------------------- + atarigen_scanline_int_ack_w: Resets the state of the + scanline interrupt. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_scanline_int_ack_w ) +{ + atarigen_scanline_int_state = 0; + (*update_int_callback)(); +} + +WRITE32_HANDLER( atarigen_scanline_int_ack32_w ) +{ + atarigen_scanline_int_state = 0; + (*update_int_callback)(); +} + + +/*--------------------------------------------------------------- + atarigen_sound_int_gen: Standard interrupt routine which + sets the sound interrupt state. +---------------------------------------------------------------*/ + +INTERRUPT_GEN( atarigen_sound_int_gen ) +{ + atarigen_sound_int_state = 1; + (*update_int_callback)(); +} + + +/*--------------------------------------------------------------- + atarigen_sound_int_ack_w: Resets the state of the sound + interrupt. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_sound_int_ack_w ) +{ + atarigen_sound_int_state = 0; + (*update_int_callback)(); +} + +WRITE32_HANDLER( atarigen_sound_int_ack32_w ) +{ + atarigen_sound_int_state = 0; + (*update_int_callback)(); +} + + +/*--------------------------------------------------------------- + atarigen_video_int_gen: Standard interrupt routine which + sets the video interrupt state. +---------------------------------------------------------------*/ + +INTERRUPT_GEN( atarigen_video_int_gen ) +{ + atarigen_video_int_state = 1; + (*update_int_callback)(); +} + + +/*--------------------------------------------------------------- + atarigen_video_int_ack_w: Resets the state of the video + interrupt. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_video_int_ack_w ) +{ + atarigen_video_int_state = 0; + (*update_int_callback)(); +} + +WRITE32_HANDLER( atarigen_video_int_ack32_w ) +{ + atarigen_video_int_state = 0; + (*update_int_callback)(); +} + + +/*--------------------------------------------------------------- + scanline_interrupt_callback: Signals an interrupt. +---------------------------------------------------------------*/ + +static TIMER_CALLBACK( scanline_interrupt_callback ) +{ + /* generate the interrupt */ + atarigen_scanline_int_gen(); + + /* set a new timer to go off at the same scan line next frame */ + timer_adjust(scanline_interrupt_timer, video_screen_get_frame_period(param), param, attotime_zero); +} + + + +/*########################################################################## + EEPROM HANDLING +##########################################################################*/ + +/*--------------------------------------------------------------- + atarigen_eeprom_reset: Makes sure that the unlocked state + is cleared when we reset. +---------------------------------------------------------------*/ + +void atarigen_eeprom_reset(void) +{ + eeprom_unlocked = 0; +} + + +/*--------------------------------------------------------------- + atarigen_eeprom_enable_w: Any write to this handler will + allow one byte to be written to the EEPROM data area the + next time. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_eeprom_enable_w ) +{ + eeprom_unlocked = 1; +} + +WRITE32_HANDLER( atarigen_eeprom_enable32_w ) +{ + eeprom_unlocked = 1; +} + + +/*--------------------------------------------------------------- + atarigen_eeprom_w: Writes a "word" to the EEPROM, which is + almost always accessed via the low byte of the word only. + If the EEPROM hasn't been unlocked, the write attempt is + ignored. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_eeprom_w ) +{ + if (!eeprom_unlocked) + return; + + COMBINE_DATA(&atarigen_eeprom[offset]); + eeprom_unlocked = 0; +} + +WRITE32_HANDLER( atarigen_eeprom32_w ) +{ + if (!eeprom_unlocked) + return; + + COMBINE_DATA(&atarigen_eeprom[offset * 2 + 1]); + data >>= 16; + mem_mask >>= 16; + COMBINE_DATA(&atarigen_eeprom[offset * 2]); + eeprom_unlocked = 0; +} + + +/*--------------------------------------------------------------- + atarigen_eeprom_r: Reads a "word" from the EEPROM, which is + almost always accessed via the low byte of the word only. +---------------------------------------------------------------*/ + +READ16_HANDLER( atarigen_eeprom_r ) +{ + return atarigen_eeprom[offset] | 0xff00; +} + +READ16_HANDLER( atarigen_eeprom_upper_r ) +{ + return atarigen_eeprom[offset] | 0x00ff; +} + +READ32_HANDLER( atarigen_eeprom_upper32_r ) +{ + return (atarigen_eeprom[offset * 2] << 16) | atarigen_eeprom[offset * 2 + 1] | 0x00ff00ff; +} + + +/*--------------------------------------------------------------- + nvram_handler_atarigen: Loads the EEPROM data. +---------------------------------------------------------------*/ + +NVRAM_HANDLER( atarigen ) +{ + if (read_or_write) + mame_fwrite(file, atarigen_eeprom, atarigen_eeprom_size); + else if (file) + mame_fread(file, atarigen_eeprom, atarigen_eeprom_size); + else + { + /* all 0xff's work for most games */ + memset(atarigen_eeprom, 0xff, atarigen_eeprom_size); + + /* anything else must be decompressed */ + if (atarigen_eeprom_default) + { + if (atarigen_eeprom_default[0] == 0) + decompress_eeprom_byte(atarigen_eeprom_default + 1); + else + decompress_eeprom_word(atarigen_eeprom_default + 1); + } + } +} + + +/*--------------------------------------------------------------- + decompress_eeprom_word: Used for decompressing EEPROM data + that has every other byte invalid. +---------------------------------------------------------------*/ + +void decompress_eeprom_word(const UINT16 *data) +{ + UINT16 *dest = (UINT16 *)atarigen_eeprom; + UINT16 value; + + while ((value = *data++) != 0) + { + int count = (value >> 8); + value = (value << 8) | (value & 0xff); + + while (count--) + *dest++ = value; + } +} + + +/*--------------------------------------------------------------- + decompress_eeprom_byte: Used for decompressing EEPROM data + that is byte-packed. +---------------------------------------------------------------*/ + +void decompress_eeprom_byte(const UINT16 *data) +{ + UINT8 *dest = (UINT8 *)atarigen_eeprom; + UINT16 value; + + while ((value = *data++) != 0) + { + int count = (value >> 8); + value = (value << 8) | (value & 0xff); + + while (count--) + *dest++ = value; + } +} + + + +/*########################################################################## + SLAPSTIC HANDLING +##########################################################################*/ + +INLINE void update_bank(int bank) +{ + /* if the bank has changed, copy the memory; Pit Fighter needs this */ + if (bank != atarigen_slapstic_bank) + { + /* bank 0 comes from the copy we made earlier */ + if (bank == 0) + memcpy(atarigen_slapstic, atarigen_slapstic_bank0, 0x2000); + else + memcpy(atarigen_slapstic, &atarigen_slapstic[bank * 0x1000], 0x2000); + + /* remember the current bank */ + atarigen_slapstic_bank = bank; + } +} + + +/*--------------------------------------------------------------- + atarigen_slapstic_init: Installs memory handlers for the + slapstic and sets the chip number. +---------------------------------------------------------------*/ + +void atarigen_slapstic_init(int cpunum, int base, int chipnum) +{ + atarigen_slapstic_num = chipnum; + atarigen_slapstic = NULL; + + /* if we have a chip, install it */ + if (chipnum) + { + /* initialize the slapstic */ + slapstic_init(chipnum); + + /* install the memory handlers */ + atarigen_slapstic = memory_install_read16_handler(cpunum, ADDRESS_SPACE_PROGRAM, base, base + 0x7fff, 0, 0, atarigen_slapstic_r); + atarigen_slapstic = memory_install_write16_handler(cpunum, ADDRESS_SPACE_PROGRAM, base, base + 0x7fff, 0, 0, atarigen_slapstic_w); + + /* allocate memory for a copy of bank 0 */ + atarigen_slapstic_bank0 = auto_malloc(0x2000); + memcpy(atarigen_slapstic_bank0, atarigen_slapstic, 0x2000); + } +} + + +/*--------------------------------------------------------------- + atarigen_slapstic_reset: Makes the selected slapstic number + active and resets its state. +---------------------------------------------------------------*/ + +void atarigen_slapstic_reset(void) +{ + if (atarigen_slapstic_num) + { + slapstic_reset(); + update_bank(slapstic_bank()); + } +} + + +/*--------------------------------------------------------------- + atarigen_slapstic_w: Assuming that the slapstic sits in + ROM memory space, we just simply tweak the slapstic at this + address and do nothing more. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_slapstic_w ) +{ + update_bank(slapstic_tweak(offset)); +} + + +/*--------------------------------------------------------------- + atarigen_slapstic_r: Tweaks the slapstic at the appropriate + address and then reads a word from the underlying memory. +---------------------------------------------------------------*/ + +READ16_HANDLER( atarigen_slapstic_r ) +{ + /* fetch the result from the current bank first */ + int result = atarigen_slapstic[offset & 0xfff]; + + /* then determine the new one */ + update_bank(slapstic_tweak(offset)); + return result; +} + + + +/*########################################################################## + SOUND I/O +##########################################################################*/ + +/*--------------------------------------------------------------- + atarigen_sound_io_reset: Resets the state of the sound I/O. +---------------------------------------------------------------*/ + +void atarigen_sound_io_reset(int cpu_num) +{ + /* remember which CPU is the sound CPU */ + sound_cpu_num = cpu_num; + + /* reset the internal interrupts states */ + timed_int = ym2151_int = 0; + + /* reset the sound I/O states */ + atarigen_cpu_to_sound = atarigen_sound_to_cpu = 0; + atarigen_cpu_to_sound_ready = atarigen_sound_to_cpu_ready = 0; +} + + +/*--------------------------------------------------------------- + atarigen_6502_irq_gen: Generates an IRQ signal to the 6502 + sound processor. +---------------------------------------------------------------*/ + +INTERRUPT_GEN( atarigen_6502_irq_gen ) +{ + timed_int = 1; + update_6502_irq(); +} + + +/*--------------------------------------------------------------- + atarigen_6502_irq_ack_r: Resets the IRQ signal to the 6502 + sound processor. Both reads and writes can be used. +---------------------------------------------------------------*/ + +READ8_HANDLER( atarigen_6502_irq_ack_r ) +{ + timed_int = 0; + update_6502_irq(); + return 0; +} + +WRITE8_HANDLER( atarigen_6502_irq_ack_w ) +{ + timed_int = 0; + update_6502_irq(); +} + + +/*--------------------------------------------------------------- + atarigen_ym2151_irq_gen: Sets the state of the YM2151's + IRQ line. +---------------------------------------------------------------*/ + +void atarigen_ym2151_irq_gen(int irq) +{ + ym2151_int = irq; + update_6502_irq(); +} + + +/*--------------------------------------------------------------- + atarigen_sound_reset_w: Write handler which resets the + sound CPU in response. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_sound_reset_w ) +{ + timer_call_after_resynch(0, delayed_sound_reset); +} + + +/*--------------------------------------------------------------- + atarigen_sound_reset: Resets the state of the sound CPU + manually. +---------------------------------------------------------------*/ + +void atarigen_sound_reset(void) +{ + timer_call_after_resynch(1, delayed_sound_reset); +} + + +/*--------------------------------------------------------------- + atarigen_sound_w: Handles communication from the main CPU + to the sound CPU. Two versions are provided, one with the + data byte in the low 8 bits, and one with the data byte in + the upper 8 bits. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_sound_w ) +{ + if (ACCESSING_LSB) + timer_call_after_resynch(data & 0xff, delayed_sound_w); +} + +WRITE16_HANDLER( atarigen_sound_upper_w ) +{ + if (ACCESSING_MSB) + timer_call_after_resynch((data >> 8) & 0xff, delayed_sound_w); +} + +WRITE32_HANDLER( atarigen_sound_upper32_w ) +{ + if (ACCESSING_MSB32) + timer_call_after_resynch((data >> 24) & 0xff, delayed_sound_w); +} + + +/*--------------------------------------------------------------- + atarigen_sound_r: Handles reading data communicated from the + sound CPU to the main CPU. Two versions are provided, one + with the data byte in the low 8 bits, and one with the data + byte in the upper 8 bits. +---------------------------------------------------------------*/ + +READ16_HANDLER( atarigen_sound_r ) +{ + atarigen_sound_to_cpu_ready = 0; + atarigen_sound_int_ack_w(0, 0, 0); + return atarigen_sound_to_cpu | 0xff00; +} + +READ16_HANDLER( atarigen_sound_upper_r ) +{ + atarigen_sound_to_cpu_ready = 0; + atarigen_sound_int_ack_w(0, 0, 0); + return (atarigen_sound_to_cpu << 8) | 0x00ff; +} + +READ32_HANDLER( atarigen_sound_upper32_r ) +{ + atarigen_sound_to_cpu_ready = 0; + atarigen_sound_int_ack32_w(0, 0, 0); + return (atarigen_sound_to_cpu << 24) | 0x00ffffff; +} + + +/*--------------------------------------------------------------- + atarigen_6502_sound_w: Handles communication from the sound + CPU to the main CPU. +---------------------------------------------------------------*/ + +WRITE8_HANDLER( atarigen_6502_sound_w ) +{ + timer_call_after_resynch(data, delayed_6502_sound_w); +} + + +/*--------------------------------------------------------------- + atarigen_6502_sound_r: Handles reading data communicated + from the main CPU to the sound CPU. +---------------------------------------------------------------*/ + +READ8_HANDLER( atarigen_6502_sound_r ) +{ + atarigen_cpu_to_sound_ready = 0; + cpunum_set_input_line(sound_cpu_num, INPUT_LINE_NMI, CLEAR_LINE); + return atarigen_cpu_to_sound; +} + + +/*--------------------------------------------------------------- + update_6502_irq: Called whenever the IRQ state changes. An + interrupt is generated if either atarigen_6502_irq_gen() + was called, or if the YM2151 generated an interrupt via + the atarigen_ym2151_irq_gen() callback. +---------------------------------------------------------------*/ + +void update_6502_irq(void) +{ + if (timed_int || ym2151_int) + cpunum_set_input_line(sound_cpu_num, M6502_IRQ_LINE, ASSERT_LINE); + else + cpunum_set_input_line(sound_cpu_num, M6502_IRQ_LINE, CLEAR_LINE); +} + + +/*--------------------------------------------------------------- + delayed_sound_reset: Synchronizes the sound reset command + between the two CPUs. +---------------------------------------------------------------*/ + +static TIMER_CALLBACK( delayed_sound_reset ) +{ + /* unhalt and reset the sound CPU */ + if (param == 0) + { + cpunum_set_input_line(sound_cpu_num, INPUT_LINE_HALT, CLEAR_LINE); + cpunum_set_input_line(sound_cpu_num, INPUT_LINE_RESET, PULSE_LINE); + } + + /* reset the sound write state */ + atarigen_sound_to_cpu_ready = 0; + atarigen_sound_int_ack_w(0, 0, 0); +} + + +/*--------------------------------------------------------------- + delayed_sound_w: Synchronizes a data write from the main + CPU to the sound CPU. +---------------------------------------------------------------*/ + +static TIMER_CALLBACK( delayed_sound_w ) +{ + /* warn if we missed something */ + if (atarigen_cpu_to_sound_ready) + logerror("Missed command from 68010\n"); + + /* set up the states and signal an NMI to the sound CPU */ + atarigen_cpu_to_sound = param; + atarigen_cpu_to_sound_ready = 1; + cpunum_set_input_line(sound_cpu_num, INPUT_LINE_NMI, ASSERT_LINE); + + /* allocate a high frequency timer until a response is generated */ + /* the main CPU is *very* sensistive to the timing of the response */ + cpu_boost_interleave(SOUND_TIMER_RATE, SOUND_TIMER_BOOST); +} + + +/*--------------------------------------------------------------- + delayed_6502_sound_w: Synchronizes a data write from the + sound CPU to the main CPU. +---------------------------------------------------------------*/ + +static TIMER_CALLBACK( delayed_6502_sound_w ) +{ + /* warn if we missed something */ + if (atarigen_sound_to_cpu_ready) + logerror("Missed result from 6502\n"); + + /* set up the states and signal the sound interrupt to the main CPU */ + atarigen_sound_to_cpu = param; + atarigen_sound_to_cpu_ready = 1; + atarigen_sound_int_gen(); +} + + + +/*########################################################################## + SOUND HELPERS +##########################################################################*/ + +/*--------------------------------------------------------------- + atarigen_set_vol: Scans for a particular sound chip and + changes the volume on all channels associated with it. +---------------------------------------------------------------*/ + +void atarigen_set_vol(running_machine *machine, int volume, sound_type type) +{ + int sndindex = 0; + int ch; + + for (ch = 0; ch < MAX_SOUND; ch++) + if (machine->drv->sound[ch].type == type) + { + int output; + for (output = 0; output < 2; output++) + sndti_set_output_gain(type, sndindex, output, volume / 100.0); + sndindex++; + } +} + + +/*--------------------------------------------------------------- + atarigen_set_XXXXX_vol: Sets the volume for a given type + of chip. +---------------------------------------------------------------*/ + +void atarigen_set_ym2151_vol(running_machine *machine, int volume) +{ + atarigen_set_vol(machine, volume, SOUND_YM2151); +} + +void atarigen_set_ym2413_vol(running_machine *machine, int volume) +{ + atarigen_set_vol(machine, volume, SOUND_YM2413); +} + +void atarigen_set_pokey_vol(running_machine *machine, int volume) +{ + atarigen_set_vol(machine, volume, SOUND_POKEY); +} + +void atarigen_set_tms5220_vol(running_machine *machine, int volume) +{ + atarigen_set_vol(machine, volume, SOUND_TMS5220); +} + +void atarigen_set_oki6295_vol(running_machine *machine, int volume) +{ + atarigen_set_vol(machine, volume, SOUND_OKIM6295); +} + + + +/*########################################################################## + SCANLINE TIMING +##########################################################################*/ + +/*--------------------------------------------------------------- + atarigen_scanline_timer_reset: Sets up the scanline timer. +---------------------------------------------------------------*/ + +void atarigen_scanline_timer_reset(int scrnum, atarigen_scanline_callback update_graphics, int frequency) +{ + /* set the scanline callback */ + scanline_callback = update_graphics; + scanlines_per_callback = frequency; + + /* set a timer to go off at scanline 0 */ + if (scanline_callback != NULL) + timer_adjust(scanline_timer[scrnum], video_screen_get_time_until_pos(scrnum, 0, 0), (scrnum << 16) | 0, attotime_zero); +} + + +/*--------------------------------------------------------------- + scanline_timer: Called once every n scanlines to generate + the periodic callback to the main system. +---------------------------------------------------------------*/ + +static TIMER_CALLBACK( scanline_timer_callback ) +{ + int scanline = param & 0x0fff; + int scrnum = param >> 16; + + /* callback */ + if (scanline_callback != NULL) + { + (*scanline_callback)(machine, scrnum, scanline); + + /* generate another */ + scanline += scanlines_per_callback; + if (scanline >= machine->screen[scrnum].height) + scanline = 0; + timer_adjust(scanline_timer[scrnum], video_screen_get_time_until_pos(scrnum, scanline, 0), (scrnum << 16) | scanline, attotime_zero); + } +} + + + +/*########################################################################## + VIDEO CONTROLLER +##########################################################################*/ + +/*--------------------------------------------------------------- + atarivc_eof_update: Callback that slurps up data and feeds + it into the video controller registers every refresh. +---------------------------------------------------------------*/ + +static TIMER_CALLBACK( atarivc_eof_update ) +{ + int scrnum = param; + int i; + + /* echo all the commands to the video controller */ + for (i = 0; i < 0x1c; i++) + if (atarivc_eof_data[i]) + atarivc_common_w(scrnum, i, atarivc_eof_data[i]); + + /* update the scroll positions */ + atarimo_set_xscroll(0, atarivc_state.mo_xscroll); + atarimo_set_yscroll(0, atarivc_state.mo_yscroll); + + tilemap_set_scrollx(atarigen_playfield_tilemap, 0, atarivc_state.pf0_xscroll); + tilemap_set_scrolly(atarigen_playfield_tilemap, 0, atarivc_state.pf0_yscroll); + + if (atarivc_playfields > 1) + { + tilemap_set_scrollx(atarigen_playfield2_tilemap, 0, atarivc_state.pf1_xscroll); + tilemap_set_scrolly(atarigen_playfield2_tilemap, 0, atarivc_state.pf1_yscroll); + } + timer_adjust(atarivc_eof_update_timer[scrnum], video_screen_get_time_until_pos(scrnum, 0, 0), scrnum, attotime_zero); + + /* use this for debugging the video controller values */ +#if 0 + if (input_code_pressed(KEYCODE_8)) + { + static FILE *out; + if (!out) out = fopen("scroll.log", "w"); + if (out) + { + for (i = 0; i < 64; i++) + fprintf(out, "%04X ", data[i]); + fprintf(out, "\n"); + } + } +#endif +} + + +/*--------------------------------------------------------------- + atarivc_reset: Initializes the video controller. +---------------------------------------------------------------*/ + +void atarivc_reset(int scrnum, UINT16 *eof_data, int playfields) +{ + /* this allows us to manually reset eof_data to NULL if it's not used */ + atarivc_eof_data = eof_data; + atarivc_playfields = playfields; + + /* clear the RAM we use */ + memset(atarivc_data, 0, 0x40); + memset(&atarivc_state, 0, sizeof(atarivc_state)); + + /* reset the latches */ + atarivc_state.latch1 = atarivc_state.latch2 = -1; + actual_vc_latch0 = actual_vc_latch1 = -1; + + atarivc_eof_update_timer[scrnum] = timer_alloc(atarivc_eof_update); + + /* start a timer to go off a little before scanline 0 */ + if (atarivc_eof_data) + timer_adjust(atarivc_eof_update_timer[scrnum], video_screen_get_time_until_pos(scrnum, 0, 0), scrnum, attotime_zero); +} + + + +/*--------------------------------------------------------------- + atarivc_w: Handles an I/O write to the video controller. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarivc_w ) +{ + int oldword = atarivc_data[offset]; + int newword = oldword; + + COMBINE_DATA(&newword); + atarivc_common_w(0, offset, newword); /* need to support scrnum */ +} + + + +/*--------------------------------------------------------------- + atarivc_common_w: Does the bulk of the word for an I/O + write. +---------------------------------------------------------------*/ + +static void atarivc_common_w(int scrnum, offs_t offset, UINT16 newword) +{ + int oldword = atarivc_data[offset]; + atarivc_data[offset] = newword; + + /* switch off the offset */ + switch (offset) + { + /* + additional registers: + + 01 = vertical start (for centering) + 04 = horizontal start (for centering) + */ + + /* set the scanline interrupt here */ + case 0x03: + if (oldword != newword) + atarigen_scanline_int_set(scrnum, newword & 0x1ff); + break; + + /* latch enable */ + case 0x0a: + + /* reset the latches when disabled */ + atarigen_set_playfield_latch((newword & 0x0080) ? actual_vc_latch0 : -1); + atarigen_set_playfield2_latch((newword & 0x0080) ? actual_vc_latch1 : -1); + + /* check for rowscroll enable */ + atarivc_state.rowscroll_enable = (newword & 0x2000) >> 13; + + /* check for palette banking */ + if (atarivc_state.palette_bank != (((newword & 0x0400) >> 10) ^ 1)) + { + video_screen_update_partial(scrnum, video_screen_get_vpos(scrnum)); + atarivc_state.palette_bank = ((newword & 0x0400) >> 10) ^ 1; + } + break; + + /* indexed parameters */ + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1a: case 0x1b: + switch (newword & 15) + { + case 9: + atarivc_state.mo_xscroll = (newword >> 7) & 0x1ff; + break; + + case 10: + atarivc_state.pf1_xscroll_raw = (newword >> 7) & 0x1ff; + atarivc_update_pf_xscrolls(); + break; + + case 11: + atarivc_state.pf0_xscroll_raw = (newword >> 7) & 0x1ff; + atarivc_update_pf_xscrolls(); + break; + + case 13: + atarivc_state.mo_yscroll = (newword >> 7) & 0x1ff; + break; + + case 14: + atarivc_state.pf1_yscroll = (newword >> 7) & 0x1ff; + break; + + case 15: + atarivc_state.pf0_yscroll = (newword >> 7) & 0x1ff; + break; + } + break; + + /* latch 1 value */ + case 0x1c: + actual_vc_latch0 = -1; + actual_vc_latch1 = newword; + atarigen_set_playfield_latch((atarivc_data[0x0a] & 0x80) ? actual_vc_latch0 : -1); + atarigen_set_playfield2_latch((atarivc_data[0x0a] & 0x80) ? actual_vc_latch1 : -1); + break; + + /* latch 2 value */ + case 0x1d: + actual_vc_latch0 = newword; + actual_vc_latch1 = -1; + atarigen_set_playfield_latch((atarivc_data[0x0a] & 0x80) ? actual_vc_latch0 : -1); + atarigen_set_playfield2_latch((atarivc_data[0x0a] & 0x80) ? actual_vc_latch1 : -1); + break; + + /* scanline IRQ ack here */ + case 0x1e: + atarigen_scanline_int_ack_w(0, 0, 0); + break; + + /* log anything else */ + case 0x00: + default: + if (oldword != newword) + logerror("vc_w(%02X, %04X) ** [prev=%04X]\n", offset, newword, oldword); + break; + } +} + + +/*--------------------------------------------------------------- + atarivc_r: Handles an I/O read from the video controller. +---------------------------------------------------------------*/ + +READ16_HANDLER( atarivc_r ) +{ + logerror("vc_r(%02X)\n", offset); + + /* a read from offset 0 returns the current scanline */ + /* also sets bit 0x4000 if we're in VBLANK */ + if (offset == 0) + { + int result = video_screen_get_vpos(0); /* need to support scrum */ + + if (result > 255) + result = 255; + if (result > Machine->screen[0].visarea.max_y) /* need to support scrum */ + result |= 0x4000; + + return result; + } + else + return atarivc_data[offset]; +} + + + +/*########################################################################## + PLAYFIELD/ALPHA MAP HELPERS +##########################################################################*/ + +/*--------------------------------------------------------------- + atarigen_alpha_w: Generic write handler for alpha RAM. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_alpha_w ) +{ + COMBINE_DATA(&atarigen_alpha[offset]); + tilemap_mark_tile_dirty(atarigen_alpha_tilemap, offset); +} + +WRITE32_HANDLER( atarigen_alpha32_w ) +{ + COMBINE_DATA(&atarigen_alpha32[offset]); + if ((mem_mask & 0xffff0000) != 0xffff0000) + tilemap_mark_tile_dirty(atarigen_alpha_tilemap, offset * 2); + if ((mem_mask & 0x0000ffff) != 0x0000ffff) + tilemap_mark_tile_dirty(atarigen_alpha_tilemap, offset * 2 + 1); +} + +WRITE16_HANDLER( atarigen_alpha2_w ) +{ + COMBINE_DATA(&atarigen_alpha2[offset]); + tilemap_mark_tile_dirty(atarigen_alpha2_tilemap, offset); +} + + + +/*--------------------------------------------------------------- + atarigen_set_playfield_latch: Sets the latch for the latched + playfield handlers below. +---------------------------------------------------------------*/ + +void atarigen_set_playfield_latch(int data) +{ + playfield_latch = data; +} + +void atarigen_set_playfield2_latch(int data) +{ + playfield2_latch = data; +} + + + +/*--------------------------------------------------------------- + atarigen_playfield_w: Generic write handler for PF RAM. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_playfield_w ) +{ + COMBINE_DATA(&atarigen_playfield[offset]); + tilemap_mark_tile_dirty(atarigen_playfield_tilemap, offset); +} + +WRITE32_HANDLER( atarigen_playfield32_w ) +{ + COMBINE_DATA(&atarigen_playfield32[offset]); + if ((mem_mask & 0xffff0000) != 0xffff0000) + tilemap_mark_tile_dirty(atarigen_playfield_tilemap, offset * 2); + if ((mem_mask & 0x0000ffff) != 0x0000ffff) + tilemap_mark_tile_dirty(atarigen_playfield_tilemap, offset * 2 + 1); +} + +WRITE16_HANDLER( atarigen_playfield2_w ) +{ + COMBINE_DATA(&atarigen_playfield2[offset]); + tilemap_mark_tile_dirty(atarigen_playfield2_tilemap, offset); +} + + + +/*--------------------------------------------------------------- + atarigen_playfield_large_w: Generic write handler for + large (2-word) playfield RAM. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_playfield_large_w ) +{ + COMBINE_DATA(&atarigen_playfield[offset]); + tilemap_mark_tile_dirty(atarigen_playfield_tilemap, offset / 2); +} + + + +/*--------------------------------------------------------------- + atarigen_playfield_upper_w: Generic write handler for + upper word of split playfield RAM. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_playfield_upper_w ) +{ + COMBINE_DATA(&atarigen_playfield_upper[offset]); + tilemap_mark_tile_dirty(atarigen_playfield_tilemap, offset); +} + + + +/*--------------------------------------------------------------- + atarigen_playfield_dual_upper_w: Generic write handler for + upper word of split dual playfield RAM. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_playfield_dual_upper_w ) +{ + COMBINE_DATA(&atarigen_playfield_upper[offset]); + tilemap_mark_tile_dirty(atarigen_playfield_tilemap, offset); + tilemap_mark_tile_dirty(atarigen_playfield2_tilemap, offset); +} + + + +/*--------------------------------------------------------------- + atarigen_playfield_latched_lsb_w: Generic write handler for + lower word of playfield RAM with a latch in the LSB of the + upper word. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_playfield_latched_lsb_w ) +{ + COMBINE_DATA(&atarigen_playfield[offset]); + tilemap_mark_tile_dirty(atarigen_playfield_tilemap, offset); + + if (playfield_latch != -1) + atarigen_playfield_upper[offset] = (atarigen_playfield_upper[offset] & ~0x00ff) | (playfield_latch & 0x00ff); +} + + + +/*--------------------------------------------------------------- + atarigen_playfield_latched_lsb_w: Generic write handler for + lower word of playfield RAM with a latch in the MSB of the + upper word. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_playfield_latched_msb_w ) +{ + COMBINE_DATA(&atarigen_playfield[offset]); + tilemap_mark_tile_dirty(atarigen_playfield_tilemap, offset); + + if (playfield_latch != -1) + atarigen_playfield_upper[offset] = (atarigen_playfield_upper[offset] & ~0xff00) | (playfield_latch & 0xff00); +} + + + +/*--------------------------------------------------------------- + atarigen_playfield_latched_lsb_w: Generic write handler for + lower word of second playfield RAM with a latch in the MSB + of the upper word. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_playfield2_latched_msb_w ) +{ + COMBINE_DATA(&atarigen_playfield2[offset]); + tilemap_mark_tile_dirty(atarigen_playfield2_tilemap, offset); + + if (playfield2_latch != -1) + atarigen_playfield_upper[offset] = (atarigen_playfield_upper[offset] & ~0xff00) | (playfield2_latch & 0xff00); +} + + + + +/*########################################################################## + VIDEO HELPERS +##########################################################################*/ + +/*--------------------------------------------------------------- + atarigen_get_hblank: Returns a guesstimate about the current + HBLANK state, based on the assumption that HBLANK represents + 10% of the scanline period. +---------------------------------------------------------------*/ + +int atarigen_get_hblank(running_machine *machine, int scrnum) +{ + return (video_screen_get_hpos(scrnum) > (machine->screen[scrnum].width * 9 / 10)); +} + + +/*--------------------------------------------------------------- + atarigen_halt_until_hblank_0_w: Halts CPU 0 until the + next HBLANK. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_halt_until_hblank_0_w ) +{ + /* halt the CPU until the next HBLANK */ + int hpos = video_screen_get_hpos(0); /* need to support scrnum */ + int hblank = Machine->screen[0].width * 9 / 10; + double fraction; + + /* if we're in hblank, set up for the next one */ + if (hpos >= hblank) + hblank += Machine->screen[0].width; + + /* halt and set a timer to wake up */ + fraction = (double)(hblank - hpos) / (double)Machine->screen[0].width; + timer_set(double_to_attotime(attotime_to_double(video_screen_get_scan_period(0)) * fraction), 0, unhalt_cpu); + cpunum_set_input_line(0, INPUT_LINE_HALT, ASSERT_LINE); +} + + +/*--------------------------------------------------------------- + atarigen_666_paletteram_w: 6-6-6 RGB palette RAM handler. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_666_paletteram_w ) +{ + int newword, r, g, b; + + COMBINE_DATA(&paletteram16[offset]); + newword = paletteram16[offset]; + + r = ((newword >> 9) & 0x3e) | ((newword >> 15) & 1); + g = ((newword >> 4) & 0x3e) | ((newword >> 15) & 1); + b = ((newword << 1) & 0x3e) | ((newword >> 15) & 1); + + palette_set_color_rgb(Machine, offset, pal6bit(r), pal6bit(g), pal6bit(b)); +} + + +/*--------------------------------------------------------------- + atarigen_expanded_666_paletteram_w: 6-6-6 RGB expanded + palette RAM handler. +---------------------------------------------------------------*/ + +WRITE16_HANDLER( atarigen_expanded_666_paletteram_w ) +{ + COMBINE_DATA(&paletteram16[offset]); + + if (ACCESSING_MSB) + { + int palentry = offset / 2; + int newword = (paletteram16[palentry * 2] & 0xff00) | (paletteram16[palentry * 2 + 1] >> 8); + + int r, g, b; + + r = ((newword >> 9) & 0x3e) | ((newword >> 15) & 1); + g = ((newword >> 4) & 0x3e) | ((newword >> 15) & 1); + b = ((newword << 1) & 0x3e) | ((newword >> 15) & 1); + + palette_set_color_rgb(Machine, palentry & 0x1ff, pal6bit(r), pal6bit(g), pal6bit(b)); + } +} + + +/*--------------------------------------------------------------- + atarigen_666_paletteram32_w: 6-6-6 RGB palette RAM handler. +---------------------------------------------------------------*/ + +WRITE32_HANDLER( atarigen_666_paletteram32_w ) +{ + int newword, r, g, b; + + COMBINE_DATA(&paletteram32[offset]); + + if (ACCESSING_MSW32) + { + newword = paletteram32[offset] >> 16; + + r = ((newword >> 9) & 0x3e) | ((newword >> 15) & 1); + g = ((newword >> 4) & 0x3e) | ((newword >> 15) & 1); + b = ((newword << 1) & 0x3e) | ((newword >> 15) & 1); + + palette_set_color_rgb(Machine, offset * 2, pal6bit(r), pal6bit(g), pal6bit(b)); + } + + if (ACCESSING_LSW32) + { + newword = paletteram32[offset] & 0xffff; + + r = ((newword >> 9) & 0x3e) | ((newword >> 15) & 1); + g = ((newword >> 4) & 0x3e) | ((newword >> 15) & 1); + b = ((newword << 1) & 0x3e) | ((newword >> 15) & 1); + + palette_set_color_rgb(Machine, offset * 2 + 1, pal6bit(r), pal6bit(g), pal6bit(b)); + } +} + + +/*--------------------------------------------------------------- + unhalt_cpu: Timer callback to release the CPU from a halted state. +---------------------------------------------------------------*/ + +static TIMER_CALLBACK( unhalt_cpu ) +{ + cpunum_set_input_line(param, INPUT_LINE_HALT, CLEAR_LINE); +} + + + +/*########################################################################## + MISC HELPERS +##########################################################################*/ + +/*--------------------------------------------------------------- + atarigen_swap_mem: Inverts the bits in a region. +---------------------------------------------------------------*/ + +void atarigen_swap_mem(void *ptr1, void *ptr2, int bytes) +{ + UINT8 *p1 = ptr1; + UINT8 *p2 = ptr2; + while (bytes--) + { + int temp = *p1; + *p1++ = *p2; + *p2++ = temp; + } +} + + +/*--------------------------------------------------------------- + atarigen_blend_gfx: Takes two GFXElements and blends their + data together to form one. Then frees the second. +---------------------------------------------------------------*/ + +void atarigen_blend_gfx(running_machine *machine, int gfx0, int gfx1, int mask0, int mask1) +{ + gfx_element *gx0 = machine->gfx[gfx0]; + gfx_element *gx1 = machine->gfx[gfx1]; + int c, x, y; + + /* loop over elements */ + for (c = 0; c < gx0->total_elements; c++) + { + UINT8 *c0base = gx0->gfxdata + gx0->char_modulo * c; + UINT8 *c1base = gx1->gfxdata + gx1->char_modulo * c; + UINT32 usage = 0; + + /* loop over height */ + for (y = 0; y < gx0->height; y++) + { + UINT8 *c0 = c0base, *c1 = c1base; + + for (x = 0; x < gx0->width; x++, c0++, c1++) + { + *c0 = (*c0 & mask0) | (*c1 & mask1); + usage |= 1 << *c0; + } + c0base += gx0->line_modulo; + c1base += gx1->line_modulo; + if (gx0->pen_usage) + gx0->pen_usage[c] = usage; + } + } + + /* free the second graphics element */ + freegfx(gx1); + machine->gfx[gfx1] = NULL; +} + + +/*########################################################################## + SAVE STATE +##########################################################################*/ + +void atarigen_init_save_state(void) +{ + state_save_register_global(atarigen_scanline_int_state); + state_save_register_global(atarigen_sound_int_state); + state_save_register_global(atarigen_video_int_state); + + state_save_register_global(atarigen_cpu_to_sound_ready); + state_save_register_global(atarigen_sound_to_cpu_ready); + + state_save_register_global(atarivc_state.latch1); /* latch #1 value (-1 means disabled) */ + state_save_register_global(atarivc_state.latch2); /* latch #2 value (-1 means disabled) */ + state_save_register_global(atarivc_state.rowscroll_enable); /* true if row-scrolling is enabled */ + state_save_register_global(atarivc_state.palette_bank); /* which palette bank is enabled */ + state_save_register_global(atarivc_state.pf0_xscroll); /* playfield 1 xscroll */ + state_save_register_global(atarivc_state.pf0_xscroll_raw); /* playfield 1 xscroll raw value */ + state_save_register_global(atarivc_state.pf0_yscroll); /* playfield 1 yscroll */ + state_save_register_global(atarivc_state.pf1_xscroll); /* playfield 2 xscroll */ + state_save_register_global(atarivc_state.pf1_xscroll_raw); /* playfield 2 xscroll raw value */ + state_save_register_global(atarivc_state.pf1_yscroll); /* playfield 2 yscroll */ + state_save_register_global(atarivc_state.mo_xscroll); /* sprite xscroll */ + state_save_register_global(atarivc_state.mo_yscroll); /* sprite xscroll */ + + state_save_register_global(eeprom_unlocked); + + state_save_register_global(atarigen_slapstic_num); + state_save_register_global(atarigen_slapstic_bank); + + state_save_register_global(sound_cpu_num); + state_save_register_global(atarigen_cpu_to_sound); + state_save_register_global(atarigen_sound_to_cpu); + state_save_register_global(timed_int); + state_save_register_global(ym2151_int); + + state_save_register_global(scanlines_per_callback); + + state_save_register_global(actual_vc_latch0); + state_save_register_global(actual_vc_latch1); + + state_save_register_global(playfield_latch); + state_save_register_global(playfield2_latch); +} |