/*########################################################################## 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 MAME_TIME_IN_USEC(5) #define SOUND_TIMER_BOOST MAME_TIME_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 mame_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 mame_timer * scanline_timer[ATARIMO_MAX]; static mame_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, int 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] = mame_timer_alloc(scanline_timer_callback); /* create a timer for scanlines */ scanline_interrupt_timer = mame_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) { mame_timer_adjust(scanline_interrupt_timer, video_screen_get_time_until_pos(scrnum, scanline, 0), scrnum, time_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 */ mame_timer_adjust(scanline_interrupt_timer, video_screen_get_frame_period(param), param, time_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. ---------------------------------------------------------------*/ static void atarigen_set_vol(running_machine *machine, int volume, int type) { int sndindex = 0; int ch; for (ch = 0; ch < MAX_SOUND; ch++) if (machine->drv->sound[ch].sound_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) mame_timer_adjust(scanline_timer[scrnum], video_screen_get_time_until_pos(scrnum, 0, 0), (scrnum << 16) | 0, time_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; mame_timer_adjust(scanline_timer[scrnum], video_screen_get_time_until_pos(scrnum, scanline, 0), (scrnum << 16) | scanline, time_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); } mame_timer_adjust(atarivc_eof_update_timer[scrnum], video_screen_get_time_until_pos(scrnum, 0, 0), scrnum, time_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] = mame_timer_alloc(atarivc_eof_update); /* start a timer to go off a little before scanline 0 */ if (atarivc_eof_data) mame_timer_adjust(atarivc_eof_update_timer[scrnum], video_screen_get_time_until_pos(scrnum, 0, 0), scrnum, time_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; mame_timer_set(double_to_mame_time(mame_time_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); }