From 7198a00e65c3437cfbb703806f82384d6fa49d1f Mon Sep 17 00:00:00 2001 From: Zsolt Vasvari Date: Sat, 1 Mar 2008 15:50:12 +0000 Subject: [PATCH] - Moves all video timing logic from cpuexec.c to video.c - Added a video_screen_register_vbl_cb() function for registering VBLANK callbanks - Changed inptport.c and debugcpu.c to make use the VBLANK callbacks - Added video_screen_get_time_until_vblank_start() - CCPU and anything using cpu_scalebyfcount() are currently broken - I did some fairly extensive testing, but this is a very signficant internal change, so some things may have broke --- src/emu/cpu/ccpu/ccpu.c | 14 +- src/emu/cpuexec.c | 391 +++++++++++++-------------------------- src/emu/debug/debugcpu.c | 37 ++-- src/emu/debug/debugcpu.h | 1 - src/emu/inptport.c | 48 +++-- src/emu/inptport.h | 3 +- src/emu/mame.c | 13 +- src/emu/validity.c | 20 +- src/emu/video.c | 248 +++++++++++++++++++------ src/emu/video.h | 17 +- 10 files changed, 402 insertions(+), 390 deletions(-) diff --git a/src/emu/cpu/ccpu/ccpu.c b/src/emu/cpu/ccpu/ccpu.c index d10de2d90d1..484361e15bc 100644 --- a/src/emu/cpu/ccpu/ccpu.c +++ b/src/emu/cpu/ccpu/ccpu.c @@ -549,13 +549,13 @@ static int ccpu_execute(int cycles) remove section in brackets and uncomment line below. Speed Freak should run nice and smooth, with no flickering. */ { - attotime scantime, abstime; - extern emu_timer *refresh_timer; - scantime = timer_starttime(refresh_timer); - abstime = timer_get_time(); - while (attotime_compare(abstime, scantime) >= 0) - scantime = attotime_add(scantime, video_screen_get_frame_period(ccpu.scrnum)); - cpu_spinuntil_time(attotime_sub(scantime, abstime)); +// attotime scantime, abstime; +// extern emu_timer *refresh_timer; +// scantime = timer_starttime(refresh_timer); +// abstime = timer_get_time(); +// while (attotime_compare(abstime, scantime) >= 0) +// scantime = attotime_add(scantime, video_screen_get_frame_period(ccpu.scrnum)); +// cpu_spinuntil_time(attotime_sub(scantime, abstime)); } /* cpu_spinuntil_time(video_screen_get_time_until_pos(ccpu.scrnum, Machine->screen[ccpu.scrnum].visarea.max_y + 1, 0)); */ NEXT_ACC_A(); CYCLES(1); diff --git a/src/emu/cpuexec.c b/src/emu/cpuexec.c index 05dd4e7b10b..7f0ef272752 100644 --- a/src/emu/cpuexec.c +++ b/src/emu/cpuexec.c @@ -77,18 +77,17 @@ struct _cpuexec_data UINT8 nexteatcycles; /* pending value */ INT32 trigger; /* pending trigger to release a trigger suspension */ - INT32 iloops; /* number of interrupts remaining this frame */ - UINT64 totalcycles; /* total CPU cycles executed */ attotime localtime; /* local time, relative to the timer system's global time */ INT32 clock; /* current active clock */ double clockscale; /* current active clock scale factor */ - INT32 vblankint_countdown; /* number of vblank callbacks left until we interrupt */ - INT32 vblankint_multiplier; /* number of vblank callbacks per interrupt */ - emu_timer * vblankint_timer; /* reference to elapsed time counter */ - emu_timer * timedint_timer; /* reference to this CPU's periodic interrupt timer */ + + /* these below are hacks to support multiple interrupts per frame */ + INT32 iloops; /* number of interrupts remaining this frame */ + emu_timer * partial_frame_timer; /* the timer that triggers partial frame interrupts */ + attotime partial_frame_period; /* the length of one partial frame for interrupt purposes */ }; @@ -100,7 +99,6 @@ struct _cpuexec_data /* general CPU variables */ static cpuexec_data cpu[MAX_CPU]; -static UINT8 vblank; static UINT32 current_frame; static int cycles_running; @@ -108,16 +106,6 @@ static int cycles_stolen; /* timer variables */ -static emu_timer *vblank_timer; -static INT32 vblank_countdown; -static INT32 vblank_multiplier; -static attotime vblank_period; - -static emu_timer *update_timer; - -emu_timer *refresh_timer; /* temporarily made non-static (for ccpu) */ -static attotime refresh_period; - static emu_timer *timeslice_timer; static attotime timeslice_period; @@ -139,14 +127,10 @@ static emu_timer *watchdog_timer; static void cpuexec_exit(running_machine *machine); static void cpuexec_reset(running_machine *machine); static void cpu_inittimers(running_machine *machine); -static void cpu_vblankreset(running_machine *machine); -static TIMER_CALLBACK( cpu_vblankcallback ); -static TIMER_CALLBACK( cpu_updatecallback ); static TIMER_CALLBACK( end_interleave_boost ); +static TIMER_CALLBACK( trigger_partial_frame_interrupt ); static void compute_perfect_interleave(running_machine *machine); -void cpu_compute_vblank_timing(running_machine *machine); - /*************************************************************************** @@ -160,19 +144,8 @@ void cpu_compute_vblank_timing(running_machine *machine); void cpuexec_init(running_machine *machine) { - int numscreens = video_screen_count(machine->config); int cpunum; - /* if there has been no VBLANK time specified in the MACHINE_DRIVER, compute it now - from the visible area */ - if (numscreens > 0 && machine->screen[0].vblank == 0 && !machine->screen[0].oldstyle_vblank_supplied) - machine->screen[0].vblank = (machine->screen[0].refresh / machine->screen[0].height) * (machine->screen[0].height - (machine->screen[0].visarea.max_y + 1 - machine->screen[0].visarea.min_y)); - - /* allocate vblank and refresh timers, and compute the initial timing */ - vblank_timer = timer_alloc(cpu_vblankcallback, NULL); - refresh_timer = timer_alloc(NULL, NULL); - cpu_compute_vblank_timing(machine); - /* loop over all our CPUs */ for (cpunum = 0; cpunum < MAX_CPU; cpunum++) { @@ -209,8 +182,6 @@ void cpuexec_init(running_machine *machine) state_save_register_item("cpu", cpunum, cpu[cpunum].clock); state_save_register_item("cpu", cpunum, cpu[cpunum].clockscale); - state_save_register_item("cpu", cpunum, cpu[cpunum].vblankint_countdown); - /* initialize this CPU */ state_save_push_tag(cpunum + 1); num_regs = state_save_get_reg_count(); @@ -235,11 +206,9 @@ void cpuexec_init(running_machine *machine) /* save some stuff in the default tag */ state_save_push_tag(0); - state_save_register_item("cpu", 0, vblank); state_save_register_item("cpu", 0, current_frame); state_save_register_item("cpu", 0, watchdog_enabled); state_save_register_item("cpu", 0, watchdog_counter); - state_save_register_item("cpu", 0, vblank_countdown); state_save_pop_tag(); } @@ -278,8 +247,6 @@ static void cpuexec_reset(running_machine *machine) } /* reset the globals */ - cpu_vblankreset(machine); - vblank = 0; current_frame = 0; } @@ -857,33 +824,6 @@ void watchdog_enable(running_machine *machine, int enable) CHEESY FAKE VIDEO TIMING ***************************************************************************/ -/*------------------------------------------------- - cpu_compute_vblank_timing - recompute cheesy - VBLANK timing after a video change --------------------------------------------------*/ - -void cpu_compute_vblank_timing(running_machine *machine) -{ - int numscreens = video_screen_count(machine->config); - attoseconds_t refresh_attosecs; - - refresh_attosecs = (numscreens == 0) ? HZ_TO_ATTOSECONDS(60) : machine->screen[0].refresh; - refresh_period = attotime_make(0, refresh_attosecs); - - /* recompute the vblank period */ - vblank_period = attotime_make(0, refresh_attosecs / (vblank_multiplier ? vblank_multiplier : 1)); - if (vblank_timer != NULL && timer_enable(vblank_timer, FALSE)) - { - attotime remaining = timer_timeleft(vblank_timer); - if (remaining.seconds == 0 && remaining.attoseconds == 0) - remaining = vblank_period; - timer_adjust_periodic(vblank_timer, remaining, 0, vblank_period); - } - - LOG(("cpu_compute_vblank_timing: refresh=%s vblank=%s\n", attotime_string(refresh_period, 9), attotime_string(vblank_period, 9))); -} - - /*------------------------------------------------- cpu_scalebyfcount - scale by time between refresh timers @@ -891,18 +831,19 @@ void cpu_compute_vblank_timing(running_machine *machine) int cpu_scalebyfcount(int value) { - attotime refresh_elapsed = timer_timeelapsed(refresh_timer); - int result; +// attotime refresh_elapsed = timer_timeelapsed(refresh_timer); +// int result; /* shift off some bits to ensure no overflow */ - if (value < 65536) - result = value * (refresh_elapsed.attoseconds >> 16) / (refresh_period.attoseconds >> 16); - else - result = value * (refresh_elapsed.attoseconds >> 32) / (refresh_period.attoseconds >> 32); - if (value >= 0) - return (result < value) ? result : value; - else - return (result > value) ? result : value; +// if (value < 65536) +// result = value * (refresh_elapsed.attoseconds >> 16) / (refresh_period.attoseconds >> 16); +// else +// result = value * (refresh_elapsed.attoseconds >> 32) / (refresh_period.attoseconds >> 32); +// if (value >= 0) +// return (result < value) ? result : value; +// else +// return (result > value) ? result : value; + return 0; } @@ -918,17 +859,6 @@ int cpu_getiloops(void) } -/*------------------------------------------------- - cpu_getvblank - return the cheesy VBLANK - state (deprecated) --------------------------------------------------*/ - -int cpu_getvblank(void) -{ - return vblank; -} - - /*------------------------------------------------- cpu_getcurrentframe - return the current frame count (deprecated) @@ -940,147 +870,121 @@ int cpu_getcurrentframe(void) } - /*************************************************************************** INTERNAL TIMING ***************************************************************************/ /*------------------------------------------------- - cpu_vblankreset - hook for updating things on - cheesy fake VBLANK (once per frame) + on_global_vblank - performs VBLANK related + housekeeping -------------------------------------------------*/ -static void cpu_vblankreset(running_machine *machine) +static void on_global_vblank(running_machine *machine, screen_state *screen, int vblank_state) { - int cpunum; - - /* notify the video system of a VBLANK start */ - video_vblank_start(machine); - - /* read keyboard & update the status of the input ports */ - input_port_vblank_start(); - - /* check the watchdog */ - if (machine->config->watchdog_vblank_count != 0 && --watchdog_counter == 0) - watchdog_callback(machine, NULL, 0); - - /* reset the cycle counters */ - for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++) + /* VBLANK starting */ + if (vblank_state) { - if (!(cpu[cpunum].suspend & SUSPEND_REASON_DISABLE)) - cpu[cpunum].iloops = machine->config->cpu[cpunum].vblank_interrupts_per_frame - 1; - else - cpu[cpunum].iloops = -1; + /* check the watchdog */ + if (machine->config->watchdog_vblank_count != 0 && --watchdog_counter == 0) + watchdog_callback(machine, NULL, 0); } + + /* VBLANK ending - increment total frames */ + else + current_frame++; } /*------------------------------------------------- - cpu_firstvblankcallback - timer callback for - the first fake VBLANK + on_per_screen_vblank - calls any external + callbacks for this screen -------------------------------------------------*/ -static TIMER_CALLBACK( cpu_firstvblankcallback ) +static void on_per_screen_vblank(running_machine *machine, screen_state *screen, int vblank_state) { - /* now that we're synced up, pulse from here on out */ - timer_adjust_periodic(vblank_timer, vblank_period, param, vblank_period); - - /* but we need to call the standard routine as well */ - cpu_vblankcallback(machine, NULL, param); -} - - -/*------------------------------------------------- - cpu_vblankcallback - timer callback for - subsequent fake VBLANKs --------------------------------------------------*/ - -static TIMER_CALLBACK( cpu_vblankcallback ) -{ - int cpunum; - - if (vblank_countdown == 1) - vblank = 1; - - /* loop over CPUs */ - for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++) + /* VBLANK starting */ + if (vblank_state) { - /* if the interrupt multiplier is valid */ - if (cpu[cpunum].vblankint_multiplier != -1) - { - /* decrement; if we hit zero, generate the interrupt and reset the countdown */ - if (!--cpu[cpunum].vblankint_countdown) - { - /* a param of -1 means don't call any callbacks */ - if (param != -1) - { - /* if the CPU has a VBLANK handler, call it */ - if (machine->config->cpu[cpunum].vblank_interrupt && !cpunum_is_suspended(cpunum, SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE)) - { - cpuintrf_push_context(cpunum); - (*machine->config->cpu[cpunum].vblank_interrupt)(machine, cpunum); - cpuintrf_pop_context(); - } + int cpunum; + const char *screen_tag = NULL; + const device_config *device; - /* update the counters */ - cpu[cpunum].iloops--; + /* get the screen's tag */ + for (device = video_screen_first(machine->config); device != NULL; device = video_screen_next(device)) + if (device->token == screen) + screen_tag = device->tag; + + assert(screen_tag != NULL); + + /* find any CPUs that have this screen as their VBLANK interrupt source */ + for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++) + { + int cpu_interested; + const cpu_config *config = machine->config->cpu + cpunum; + + /* start the interrupt counter */ + if (!(cpu[cpunum].suspend & SUSPEND_REASON_DISABLE)) + cpu[cpunum].iloops = config->vblank_interrupts_per_frame - 1; + else + cpu[cpunum].iloops = -1; + + /* the hack style VBLANK decleration uses the first screen always */ + if (config->vblank_interrupts_per_frame > 1) + cpu_interested = TRUE; + + /* for new style decleration, we need to compare the tags */ + else if (config->vblank_interrupts_per_frame == 1) + cpu_interested = (strcmp(config->vblank_interrupt_screen, screen_tag) == 0); + + /* no VBLANK interrupt, not interested */ + else + cpu_interested = FALSE; + + /* if interested, call the interrupt handler */ + if (cpu_interested) + { + if (!cpunum_is_suspended(cpunum, SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE)) + { + cpuintrf_push_context(cpunum); + (*machine->config->cpu[cpunum].vblank_interrupt)(machine, cpunum); + cpuintrf_pop_context(); } - /* reset the countdown and timer */ - cpu[cpunum].vblankint_countdown = cpu[cpunum].vblankint_multiplier; - timer_adjust_oneshot(cpu[cpunum].vblankint_timer, attotime_never, 0); + /* if we have more than one interrupt per frame, start the timer now to trigger the rest of them */ + if ((config->vblank_interrupts_per_frame > 1) && + !(cpu[cpunum].suspend & SUSPEND_REASON_DISABLE)) + { + cpu[cpunum].partial_frame_period = attotime_div(video_screen_get_frame_period(0), config->vblank_interrupts_per_frame); + timer_adjust_oneshot(cpu[cpunum].partial_frame_timer, cpu[cpunum].partial_frame_period, cpunum); + } } } - - /* else reset the VBLANK timer if this is going to be a real VBLANK */ - else if (vblank_countdown == 1) - timer_adjust_oneshot(cpu[cpunum].vblankint_timer, attotime_never, 0); - } - - /* is it a real VBLANK? */ - if (!--vblank_countdown) - { - /* do we update the screen now? */ - if (!(machine->config->video_attributes & VIDEO_UPDATE_AFTER_VBLANK)) - video_frame_update(machine, FALSE); - - /* Set the timer to update the screen */ - timer_adjust_oneshot(update_timer, attotime_make(0, machine->screen[0].vblank), 0); - - /* reset the globals */ - cpu_vblankreset(machine); - - /* reset the counter */ - vblank_countdown = vblank_multiplier; - -#ifdef ENABLE_DEBUGGER - /* notify the debugger */ - debug_vblank_hook(); -#endif } } /*------------------------------------------------- - cpu_updatecallback - timer callback for - the fake end of VBLANK + trigger_partial_frame_interrupt - called to + trigger a partial frame interrupt -------------------------------------------------*/ -static TIMER_CALLBACK( cpu_updatecallback ) +static TIMER_CALLBACK( trigger_partial_frame_interrupt ) { - /* update the screen if we didn't before */ - if (machine->config->video_attributes & VIDEO_UPDATE_AFTER_VBLANK) - video_frame_update(machine, FALSE); - vblank = 0; + int cpunum = param; - /* update IPT_VBLANK input ports */ - input_port_vblank_end(); + cpu[cpunum].iloops--; - /* track total frames */ - current_frame++; + /* call the interrupt handler */ + if (!cpunum_is_suspended(cpunum, SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE)) + { + cpuintrf_push_context(cpunum); + (*machine->config->cpu[cpunum].vblank_interrupt)(machine, cpunum); + cpuintrf_pop_context(); + } - /* reset the refresh timer */ - timer_adjust_oneshot(refresh_timer, attotime_never, 0); + /* more? */ + if (cpu[cpunum].iloops > 0) + timer_adjust_oneshot(cpu[cpunum].partial_frame_timer, cpu[cpunum].partial_frame_period, cpunum); } @@ -1165,8 +1069,7 @@ static void cpu_inittimers(running_machine *machine) { int numscreens = video_screen_count(machine->config); attoseconds_t refresh_attosecs; - attotime first_time; - int cpunum, max, ipf; + int cpunum, ipf; /* allocate a timer for the watchdog */ watchdog_timer = timer_alloc(watchdog_callback, NULL); @@ -1184,68 +1087,44 @@ static void cpu_inittimers(running_machine *machine) interleave_boost_timer = timer_alloc(NULL, NULL); interleave_boost_timer_end = timer_alloc(end_interleave_boost, NULL); - /* - * The following code finds all the CPUs that are interrupting in sync with the VBLANK - * and sets up the VBLANK timer to run at the minimum number of cycles per frame in - * order to service all the synced interrupts - */ - - /* find the CPU with the maximum interrupts per frame */ - max = 1; + /* register the screen specific VBLANK callbacks */ for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++) { - ipf = machine->config->cpu[cpunum].vblank_interrupts_per_frame; - if (ipf > max) - max = ipf; - } + const cpu_config *config = machine->config->cpu + cpunum; - /* now find the LCD with the rest of the CPUs (brute force - these numbers aren't huge) */ - vblank_multiplier = max; - while (1) - { - for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++) + if (config->vblank_interrupts_per_frame > 0) { - ipf = machine->config->cpu[cpunum].vblank_interrupts_per_frame; - if (ipf > 0 && (vblank_multiplier % ipf) != 0) - break; + const char *screen_tag; + screen_state *screen; + + /* get the screen that will trigger the VBLANK */ + + /* new style - use screen tag directly */ + if (config->vblank_interrupts_per_frame == 1) + screen_tag = config->vblank_interrupt_screen; + + /* old style 'hack' setup - use screen #0 */ + else + { + const device_config *config = device_list_first(machine->config->devicelist, VIDEO_SCREEN); + screen_tag = config->tag; + + /* allocate timer that will trigger the partial frame updates */ + cpu[cpunum].partial_frame_timer = timer_alloc(trigger_partial_frame_interrupt, 0); + } + + screen = devtag_get_token(machine, VIDEO_SCREEN, screen_tag); + video_screen_register_vbl_cb(machine, screen, on_per_screen_vblank); } - if (cpunum == cpu_gettotalcpu()) - break; - vblank_multiplier += max; } - /* initialize the countdown timers and intervals */ + /* register the global VBLANK callback -- it's important to do it after the + screen specific ones */ + video_screen_register_vbl_cb(machine, NULL, on_global_vblank); + + /* start the CPU periodic interrupt timers */ for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++) { - ipf = machine->config->cpu[cpunum].vblank_interrupts_per_frame; - if (ipf > 0) - cpu[cpunum].vblankint_countdown = cpu[cpunum].vblankint_multiplier = vblank_multiplier / ipf; - else - cpu[cpunum].vblankint_countdown = cpu[cpunum].vblankint_multiplier = -1; - } - - /* allocate a vblank timer at the frame rate * the LCD number of interrupts per frame */ - vblank_period = attotime_make(0, refresh_attosecs / vblank_multiplier); - vblank_countdown = vblank_multiplier; - - /* allocate an update timer that will be used to time the actual screen updates */ - update_timer = timer_alloc(cpu_updatecallback, NULL); - - /* - * The following code creates individual timers for each CPU whose interrupts are not - * synced to the VBLANK, and computes the typical number of cycles per interrupt - */ - - /* start the CPU interrupt timers */ - for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++) - { - ipf = machine->config->cpu[cpunum].vblank_interrupts_per_frame; - - /* compute the average number of cycles per interrupt */ - if (ipf <= 0) - ipf = 1; - cpu[cpunum].vblankint_timer = timer_alloc(NULL, NULL); - /* see if we need to allocate a CPU timer */ if (machine->config->cpu[cpunum].timed_interrupt_period != 0) { @@ -1254,18 +1133,4 @@ static void cpu_inittimers(running_machine *machine) timer_adjust_periodic(cpu[cpunum].timedint_timer, timedint_period, cpunum, timedint_period); } } - - /* note that since we start the first frame on the refresh, we can't pulse starting - immediately; instead, we back up one VBLANK period, and inch forward until we hit - positive time. That time will be the time of the first VBLANK timer callback */ - first_time = attotime_sub_attoseconds(vblank_period, machine->screen[0].vblank); - while (attotime_compare(first_time, attotime_zero) < 0) - { - cpu_vblankcallback(machine, NULL, -1); - first_time = attotime_add(first_time, vblank_period); - } - timer_set(first_time, NULL, 0, cpu_firstvblankcallback); - - /* reset the refresh timer to get ourself back in sync */ - timer_adjust_oneshot(refresh_timer, attotime_never, 0); } diff --git a/src/emu/debug/debugcpu.c b/src/emu/debug/debugcpu.c index 9a8c84b3cfa..8a35e880952 100644 --- a/src/emu/debug/debugcpu.c +++ b/src/emu/debug/debugcpu.c @@ -140,6 +140,22 @@ int mame_debug_is_active(void) } +/*------------------------------------------------- + on_vblank - called when a VBLANK hits +-------------------------------------------------*/ + +static void on_vblank(running_machine *machine, screen_state *screen, int vblank_state) +{ + /* if we're configured to stop on VBLANK, break */ + if (vblank_state && break_on_vblank) + { + execution_state = EXECUTION_STATE_STOPPED; + debug_console_printf("Stopped at VBLANK\n"); + break_on_vblank = 0; + } +} + + /*************************************************************************** INITIALIZATION ***************************************************************************/ @@ -279,6 +295,10 @@ void debug_cpu_init(running_machine *machine) } } + /* add callback for breaking on VBLANK */ +fprintf(stderr,"Debug 1\n"); + video_screen_register_vbl_cb(machine, NULL, on_vblank); + add_exit_callback(machine, debug_cpu_exit); } @@ -1027,23 +1047,6 @@ static void process_source_file(void) } -/*------------------------------------------------- - debug_vblank_hook - called when the real - VBLANK hits --------------------------------------------------*/ - -void debug_vblank_hook(void) -{ - /* if we're configured to stop on VBLANK, break */ - if (break_on_vblank) - { - execution_state = EXECUTION_STATE_STOPPED; - debug_console_printf("Stopped at VBLANK\n"); - break_on_vblank = 0; - } -} - - /*------------------------------------------------- debug_vblank_hook - called when an interrupt is acknowledged diff --git a/src/emu/debug/debugcpu.h b/src/emu/debug/debugcpu.h index 1cc192f7f32..8cbffe22cfa 100644 --- a/src/emu/debug/debugcpu.h +++ b/src/emu/debug/debugcpu.h @@ -182,7 +182,6 @@ void debug_source_script(const char *file); void debug_flush_traces(void); /* debugging hooks */ -void debug_vblank_hook(void); void debug_interrupt_hook(int cpunum, int irqline); void debug_get_memory_hooks(int cpunum, debug_hook_read_ptr *read, debug_hook_write_ptr *write); void debug_set_instruction_hook(int cpunum, int (*hook)(offs_t pc)); diff --git a/src/emu/inptport.c b/src/emu/inptport.c index 93a4d4cb95f..0d93e6cba50 100644 --- a/src/emu/inptport.c +++ b/src/emu/inptport.c @@ -1034,11 +1034,14 @@ static int default_ports_lookup[__ipt_max][MAX_PLAYERS]; FUNCTION PROTOTYPES ***************************************************************************/ +static void on_vblank(running_machine *machine, screen_state *screen, int vblank_state); static void setup_playback(running_machine *machine); static void setup_record(running_machine *machine); static void input_port_exit(running_machine *machine); static void input_port_load(int config_type, xml_data_node *parentnode); static void input_port_save(int config_type, xml_data_node *parentnode); +static void input_port_vblank_start(void); +static void input_port_vblank_end(void); static void update_digital_joysticks(void); static void update_analog_port(int port); static void interpolate_analog_port(int port); @@ -1138,6 +1141,33 @@ void input_port_init(running_machine *machine, const input_port_token *ipt) } +void input_port_post_init(running_machine *machine) +{ + /* set up callback for updating the ports */ + video_screen_register_vbl_cb(machine, NULL, on_vblank); +} + + + +/************************************* + * + * VBLANK handler + * + *************************************/ + +static void on_vblank(running_machine *machine, screen_state *screen, int vblank_state) +{ + /* VBLANK starting - read keyboard & update the status of the input ports */ + if (vblank_state) + input_port_vblank_start(); + + /* VBLANK ending - update IPT_VBLANK input ports */ + else + input_port_vblank_end(); +} + + + /************************************* * * Set up for playback @@ -2827,7 +2857,7 @@ void input_port_update_defaults(void) * *************************************/ -void input_port_vblank_start(void) +static void input_port_vblank_start(void) { int ui_visible = ui_is_menu_active() || ui_is_slider_active(); int portnum, bitnum; @@ -2997,7 +3027,7 @@ profiler_mark(PROFILER_END); * *************************************/ -void input_port_vblank_end(void) +static void input_port_vblank_end(void) { int ui_visible = ui_is_menu_active() || ui_is_slider_active(); int port; @@ -3417,18 +3447,8 @@ UINT32 readinputport(int port) result = (result & ~portinfo->vblank) | (portinfo->defvalue & portinfo->vblank); /* toggle VBLANK if we're in a VBLANK state */ - if (Machine->screen[0].oldstyle_vblank_supplied) - { - int cpu_getvblank(void); - - if (cpu_getvblank()) - result ^= portinfo->vblank; - } - else - { - if (video_screen_get_vblank(0)) - result ^= portinfo->vblank; - } + if (video_screen_get_vblank(0)) + result ^= portinfo->vblank; } return result; } diff --git a/src/emu/inptport.h b/src/emu/inptport.h index 183a942a555..c8b7bae6fdf 100644 --- a/src/emu/inptport.h +++ b/src/emu/inptport.h @@ -893,6 +893,7 @@ struct _ext_inp_header ***************************************************************************/ void input_port_init(running_machine *machine, const input_port_token *ipt); +void input_port_post_init(running_machine *machine); const char *input_port_string_from_token(const input_port_token token); input_port_entry *input_port_initialize(input_port_init_params *params, UINT32 type, const char *tag, UINT32 mask, UINT32 defval); @@ -926,8 +927,6 @@ int input_ui_pressed(int code); int input_ui_pressed_repeat(int code, int speed); void input_port_update_defaults(void); -void input_port_vblank_start(void); /* called by cpuintrf.c - not for external use */ -void input_port_vblank_end(void); /* called by cpuintrf.c - not for external use */ void input_port_set_digital_value(int port, UINT32 value, UINT32 mask); diff --git a/src/emu/mame.c b/src/emu/mame.c index 5c3a5ee3de3..8ba708fb1c5 100644 --- a/src/emu/mame.c +++ b/src/emu/mame.c @@ -1573,12 +1573,6 @@ static void init_machine(running_machine *machine) devices_init(machine); #endif -#ifdef ENABLE_DEBUGGER - /* initialize the debugger */ - if (machine->debug_mode) - mame_debug_init(machine); -#endif - /* call the game driver's init function */ /* this is where decryption is done and memory maps are altered */ /* so this location in the init order is important */ @@ -1593,6 +1587,13 @@ static void init_machine(running_machine *machine) device_list_start(machine); sound_init(machine); + input_port_post_init(machine); + +#ifdef ENABLE_DEBUGGER + /* initialize the debugger */ + if (machine->debug_mode) + mame_debug_init(machine); +#endif /* call the driver's _START callbacks */ if (machine->config->machine_start != NULL) (*machine->config->machine_start)(machine); diff --git a/src/emu/validity.c b/src/emu/validity.c index ea53fe2a8f3..10332b4a63d 100644 --- a/src/emu/validity.c +++ b/src/emu/validity.c @@ -780,24 +780,10 @@ static int validate_cpu(int drivnum, const machine_config *config, const UINT32 mame_printf_error("%s: %s cpu #%d has a valid VBLANK interrupt handler with no screen tag supplied!\n", driver->source_file, driver->name, cpunum); error = TRUE; } - else + else if (device_list_index(config->devicelist, VIDEO_SCREEN, cpu->vblank_interrupt_screen) == -1) { - int screen_tag_found = FALSE; - const device_config *device; - - /* loop over screens looking for the tag */ - for (device = video_screen_first(config); device != NULL; device = video_screen_next(device)) - if (strcmp(device->tag, cpu->vblank_interrupt_screen) == 0) - { - screen_tag_found = TRUE; - break; - } - - if (!screen_tag_found) - { - mame_printf_error("%s: %s cpu #%d VBLANK interrupt with a non-existant screen tag (%s)!\n", driver->source_file, driver->name, cpunum, cpu->vblank_interrupt_screen); - error = TRUE; - } + mame_printf_error("%s: %s cpu #%d VBLANK interrupt with a non-existant screen tag (%s)!\n", driver->source_file, driver->name, cpunum, cpu->vblank_interrupt_screen); + error = TRUE; } } } diff --git a/src/emu/video.c b/src/emu/video.c index 5ee0e4e3214..1c47f7adf5b 100644 --- a/src/emu/video.c +++ b/src/emu/video.c @@ -39,7 +39,8 @@ ***************************************************************************/ #define SUBSECONDS_PER_SPEED_UPDATE (ATTOSECONDS_PER_SECOND / 4) -#define PAUSED_REFRESH_RATE 30 +#define PAUSED_REFRESH_RATE (30) +#define MAX_VBL_CB (10) @@ -51,35 +52,43 @@ typedef struct _internal_screen_info internal_screen_info; struct _internal_screen_info { /* pointers to screen configuration and state */ - const screen_config * config; /* pointer to the configuration in the Machine->config */ - screen_state * state; /* pointer to visible state in Machine structure */ + int scrnum; /* the screen index */ + const screen_config * config; /* pointer to the configuration in the Machine->config */ + screen_state * state; /* pointer to visible state in Machine structure */ /* textures and bitmaps */ - render_texture * texture[2]; /* 2x textures for the screen bitmap */ - bitmap_t * bitmap[2]; /* 2x bitmaps for rendering */ - UINT8 curbitmap; /* current bitmap index */ - UINT8 curtexture; /* current texture index */ - bitmap_format format; /* format of bitmap for this screen */ - UINT8 changed; /* has this bitmap changed? */ - INT32 last_partial_scan; /* scanline of last partial update */ + render_texture * texture[2]; /* 2x textures for the screen bitmap */ + bitmap_t * bitmap[2]; /* 2x bitmaps for rendering */ + UINT8 curbitmap; /* current bitmap index */ + UINT8 curtexture; /* current texture index */ + bitmap_format format; /* format of bitmap for this screen */ + UINT8 changed; /* has this bitmap changed? */ + INT32 last_partial_scan; /* scanline of last partial update */ + UINT8 vblank_state; /* 1 = in VBLANK region, 0 = outside */ /* screen timing */ - attoseconds_t scantime; /* attoseconds per scanline */ - attoseconds_t pixeltime; /* attoseconds per pixel */ - attotime vblank_time; /* time of last VBLANK start */ - emu_timer * scanline0_timer; /* scanline 0 timer */ - emu_timer * scanline_timer; /* scanline timer */ + attoseconds_t scantime; /* attoseconds per scanline */ + attoseconds_t pixeltime; /* attoseconds per pixel */ + attotime vblank_time; /* time of last VBLANK start */ + emu_timer * vblank_begin_timer; /* timer to signal VBLANK start */ + emu_timer * vblank_end_timer; /* timer to signal VBLANK end */ + emu_timer * scanline0_timer; /* scanline 0 timer */ + emu_timer * scanline_timer; /* scanline timer */ + + /* VBLANK callbacks */ + int vbl_cb_count; /* # of callbacks installed */ + video_screen_on_vbl vbl_cbs[MAX_VBL_CB]; /* the array of callbacks */ /* movie recording */ - mame_file * movie_file; /* handle to the open movie file */ - UINT32 movie_frame; /* current movie frame number */ + mame_file * movie_file; /* handle to the open movie file */ + UINT32 movie_frame; /* current movie frame number */ }; struct _video_private { /* per-screen information */ - internal_screen_info scrinfo[MAX_SCREENS]; /* per-screen infomration */ + internal_screen_info scrinfo[MAX_SCREENS]; /* per-screen information */ /* snapshot stuff */ render_target * snap_target; /* screen shapshot target */ @@ -176,6 +185,8 @@ static void allocate_graphics(running_machine *machine, const gfx_decode_entry * static void decode_graphics(running_machine *machine, const gfx_decode_entry *gfxdecodeinfo); /* global rendering */ +static TIMER_CALLBACK( vblank_begin_callback ); +static TIMER_CALLBACK( vblank_end_callback ); static TIMER_CALLBACK( scanline0_callback ); static TIMER_CALLBACK( scanline_update_callback ); static int finish_screen_updates(running_machine *machine); @@ -305,10 +316,15 @@ void video_init(running_machine *machine) render_container *container = render_container_get_screen(scrnum); internal_screen_info *info = &viddata->scrinfo[scrnum]; + /* allocate the VBLANK timers */ + info->vblank_begin_timer = timer_alloc(vblank_begin_callback, info); + info->vblank_end_timer = timer_alloc(vblank_end_callback, info); + /* allocate a timer to reset partial updates */ info->scanline0_timer = timer_alloc(scanline0_callback, NULL); /* make pointers back to the config and state */ + info->scrnum = scrnum; info->config = device->inline_config; info->state = &machine->screen[scrnum]; @@ -326,7 +342,7 @@ void video_init(running_machine *machine) render_container_set_yscale(container, info->config->yscale); /* reset VBLANK timing */ - info->vblank_time = attotime_sub_attoseconds(attotime_zero, machine->screen[0].vblank); + info->vblank_time = attotime_zero; /* allocate a timer to generate per-scanline updates */ if (machine->config->video_attributes & VIDEO_UPDATE_SCANLINE) @@ -433,28 +449,6 @@ static void video_exit(running_machine *machine) } -/*------------------------------------------------- - video_vblank_start - called at the start of - VBLANK, which is driven by the CPU scheduler --------------------------------------------------*/ - -void video_vblank_start(running_machine *machine) -{ - video_private *viddata = machine->video_data; - attotime curtime = timer_get_time(); - int scrnum; - - /* kludge: we get called at time 0 to reset, but at that point, - the time of last VBLANK is actually -vblank_duration */ - if (curtime.seconds == 0 && curtime.attoseconds == 0) - curtime = attotime_sub_attoseconds(attotime_zero, machine->screen[0].vblank); - - /* reset VBLANK timers for each screen -- fix me */ - for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++) - viddata->scrinfo[scrnum].vblank_time = curtime; -} - - /*------------------------------------------------- init_buffered_spriteram - initialize the double-buffered spriteram @@ -765,18 +759,15 @@ void video_screen_configure(int scrnum, int width, int height, const rectangle * } } - /* recompute the VBLANK timing */ - { - extern void cpu_compute_vblank_timing(running_machine *machine); - cpu_compute_vblank_timing(Machine); - } - /* if we are on scanline 0 already, reset the update timer immediately */ /* otherwise, defer until the next scanline 0 */ if (video_screen_get_vpos(scrnum) == 0) timer_adjust_oneshot(info->scanline0_timer, attotime_zero, scrnum); else timer_adjust_oneshot(info->scanline0_timer, video_screen_get_time_until_pos(scrnum, 0, 0), scrnum); + + /* start the VBLANK timer */ + timer_adjust_oneshot(info->vblank_begin_timer, video_screen_get_time_until_vblank_start(scrnum), 0); } @@ -973,8 +964,7 @@ int video_screen_get_hpos(int scrnum) int video_screen_get_vblank(int scrnum) { internal_screen_info *info = get_screen_info(Machine, scrnum); - int vpos = video_screen_get_vpos(scrnum); - return (vpos < info->state->visarea.min_y || vpos > info->state->visarea.max_y); + return info->vblank_state; } @@ -1021,6 +1011,18 @@ attotime video_screen_get_time_until_pos(int scrnum, int vpos, int hpos) } +/*------------------------------------------------- + video_screen_get_time_until_vblank_start - + returns the amount of time remaining until + the next VBLANK period start +-------------------------------------------------*/ + +attotime video_screen_get_time_until_vblank_start(int scrnum) +{ + return video_screen_get_time_until_pos(scrnum, Machine->screen[scrnum].visarea.max_y + 1, 0); +} + + /*------------------------------------------------- video_screen_get_scan_period - return the amount of time the beam takes to draw one @@ -1047,6 +1049,64 @@ attotime video_screen_get_frame_period(int scrnum) +/*------------------------------------------------- + video_screen_register_vbl_cb - registers a + VBLANK callback +-------------------------------------------------*/ + +void video_screen_register_vbl_cb(running_machine *machine, void *screen, video_screen_on_vbl vbl_cb) +{ + int i, found; + internal_screen_info *scrinfo = NULL; + + /* validate arguments */ + assert(machine != NULL); + assert(machine->config != NULL); + assert(machine->config->devicelist != NULL); + assert(machine->video_data != NULL); + assert(machine->video_data->scrinfo != NULL); + assert(vbl_cb != NULL); + + /* if the screen is NULL, grab the first screen -- we are installing a "global" handler */ + if (screen == NULL) + { + if (video_screen_count(machine->config) > 0) + { + const char *screen_tag = video_screen_first(machine->config)->tag; + screen = devtag_get_token(machine, VIDEO_SCREEN, screen_tag); + } + else + { + /* we need to do something for screenless games */ + assert(screen != NULL); + } + } + + /* find the screen info structure for the given token - there should only be one */ + for (i = 0; i < MAX_SCREENS; i++) + if (machine->video_data->scrinfo[i].state == screen) + scrinfo = machine->video_data->scrinfo + i; + + /* make sure we still have room for the callback */ + assert(scrinfo != NULL); + assert(scrinfo->vbl_cb_count != MAX_VBL_CB); + + /* check if we already have this callback registered */ + found = FALSE; + for (i = 0; i < scrinfo->vbl_cb_count; i++) + if (scrinfo->vbl_cbs[i] == vbl_cb) + found = TRUE; + + /* if not found, register and increment count */ + if (!found) + { + scrinfo->vbl_cbs[scrinfo->vbl_cb_count] = vbl_cb; + scrinfo->vbl_cb_count++; + } +} + + + /*************************************************************************** VIDEO SCREEN DEVICE INTERFACE ***************************************************************************/ @@ -1087,21 +1147,21 @@ DEVICE_GET_INFO( video_screen ) switch (state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ - case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(screen_config); break; - case DEVINFO_INT_CLASS: info->i = DEVICE_CLASS_VIDEO; break; + case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(screen_config); break; + case DEVINFO_INT_CLASS: info->i = DEVICE_CLASS_VIDEO; break; /* --- the following bits of info are returned as pointers to data or functions --- */ - case DEVINFO_FCT_SET_INFO: info->set_info = DEVICE_SET_INFO_NAME(video_screen); break; - case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(video_screen); break; - case DEVINFO_FCT_STOP: /* Nothing */ break; - case DEVINFO_FCT_RESET: /* Nothing */ break; + case DEVINFO_FCT_SET_INFO: info->set_info = DEVICE_SET_INFO_NAME(video_screen); break; + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(video_screen); break; + case DEVINFO_FCT_STOP: /* Nothing */ break; + case DEVINFO_FCT_RESET: /* Nothing */ break; /* --- the following bits of info are returned as NULL-terminated strings --- */ - case DEVINFO_STR_NAME: info->s = "Raster"; break; - case DEVINFO_STR_FAMILY: info->s = "Video Screen"; break; - case DEVINFO_STR_VERSION: info->s = "1.0"; break; - case DEVINFO_STR_SOURCE_FILE: info->s = __FILE__; break; - case DEVINFO_STR_CREDITS: info->s = "Copyright Nicola Salmoria and the MAME Team"; break; + case DEVINFO_STR_NAME: info->s = "Raster"; break; + case DEVINFO_STR_FAMILY: info->s = "Video Screen"; break; + case DEVINFO_STR_VERSION: info->s = "1.0"; break; + case DEVINFO_STR_SOURCE_FILE: info->s = __FILE__; break; + case DEVINFO_STR_CREDITS: info->s = "Copyright Nicola Salmoria and the MAME Team"; break; } } @@ -1111,6 +1171,74 @@ DEVICE_GET_INFO( video_screen ) GLOBAL RENDERING ***************************************************************************/ +/*------------------------------------------------- + call_vb_callbacks - call any external VBLANK + callsbacks with the given state +-------------------------------------------------*/ + +static void call_vb_callbacks(running_machine *machine, internal_screen_info *scrinfo, int vblank_state) +{ + int i; + + /* set it in the state structure */ + scrinfo->vblank_state = vblank_state; + + for (i = 0; i < scrinfo->vbl_cb_count; i++) + scrinfo->vbl_cbs[i](machine, scrinfo->state, vblank_state); +} + + +/*------------------------------------------------- + vblank_begin_callback - call any external + callbacks to signal the VBLANK period has begun +-------------------------------------------------*/ + +static TIMER_CALLBACK( vblank_begin_callback ) +{ + attoseconds_t vblank_period; + internal_screen_info *scrinfo = ptr; + + /* reset the starting VBLANK time */ + scrinfo->vblank_time = timer_get_time(); + + /* call the external callbacks */ + call_vb_callbacks(machine, scrinfo, TRUE); + + /* do we update the screen now? - only do it for the first screen */ + if (!(machine->config->video_attributes & VIDEO_UPDATE_AFTER_VBLANK) && (scrinfo->scrnum == 0)) + video_frame_update(machine, FALSE); + + /* if there has been no VBLANK time specified in the MACHINE_DRIVER, compute it now + from the visible area, otherwise just used the supplied value */ + if ((scrinfo->state->vblank == 0) && !scrinfo->state->oldstyle_vblank_supplied) + vblank_period = (scrinfo->state->refresh / scrinfo->state->height) * (scrinfo->state->height - (scrinfo->state->visarea.max_y + 1 - scrinfo->state->visarea.min_y)); + else + vblank_period = scrinfo->state->vblank; + + /* reset the timers */ + timer_adjust_oneshot(scrinfo->vblank_begin_timer, attotime_make(0, scrinfo->state->refresh), 0); + timer_adjust_oneshot(scrinfo->vblank_end_timer, attotime_make(0, vblank_period), 0); +} + + +/*------------------------------------------------- + vblank_end_callback - call any external + callbacks to signal the VBLANK period has ended +-------------------------------------------------*/ + +static TIMER_CALLBACK( vblank_end_callback ) +{ + internal_screen_info *scrinfo = ptr; + + /* update the first screen if we didn't before */ + if ((machine->config->video_attributes & VIDEO_UPDATE_AFTER_VBLANK) && (scrinfo->scrnum == 0)) + video_frame_update(machine, FALSE); + + /* let any external parties know that the VBLANK is over */ + call_vb_callbacks(machine, scrinfo, FALSE); +} + + /*------------------------------------------------- scanline0_callback - reset partial updates for a screen diff --git a/src/emu/video.h b/src/emu/video.h index a8db1303c2b..08961c4c6b5 100644 --- a/src/emu/video.h +++ b/src/emu/video.h @@ -88,6 +88,15 @@ struct _screen_config }; +/*------------------------------------------------- + video_screen_on_vblank - callback that a + screen calls to notify of a change of + the VBLANK state +-------------------------------------------------*/ + +typedef void (*video_screen_on_vbl)(running_machine *machine, screen_state *screen, int vblank_state); + + /*************************************************************************** FUNCTION PROTOTYPES @@ -98,9 +107,6 @@ struct _screen_config /* core initialization */ void video_init(running_machine *machine); -/* core VBLANK callback */ -void video_vblank_start(running_machine *machine); - /* ----- screen management ----- */ @@ -127,6 +133,9 @@ int video_screen_get_hblank(int scrnum); /* return the time when the beam will reach a particular H,V position */ attotime video_screen_get_time_until_pos(int scrnum, int vpos, int hpos); +/* return the time when the beam will reach the start of VBLANK */ +attotime video_screen_get_time_until_vblank_start(int scrnum); + /* return the amount of time the beam takes to draw one scan line */ attotime video_screen_get_scan_period(int scrnum); @@ -136,6 +145,8 @@ attotime video_screen_get_frame_period(int scrnum); /* returns whether a given screen exists */ int video_screen_exists(int scrnum); +/* registers a VBLANK callback for the given screen*/ +void video_screen_register_vbl_cb(running_machine *machine, void *screen, video_screen_on_vbl vbl_cb); /* ----- video screen device interface ----- */