- 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
This commit is contained in:
Zsolt Vasvari 2008-03-01 15:50:12 +00:00
parent e90a92697c
commit 7198a00e65
10 changed files with 402 additions and 390 deletions

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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));

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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 ----- */