- 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:
parent
e90a92697c
commit
7198a00e65
@ -549,13 +549,13 @@ static int ccpu_execute(int cycles)
|
|||||||
remove section in brackets and uncomment line below.
|
remove section in brackets and uncomment line below.
|
||||||
Speed Freak should run nice and smooth, with no flickering. */
|
Speed Freak should run nice and smooth, with no flickering. */
|
||||||
{
|
{
|
||||||
attotime scantime, abstime;
|
// attotime scantime, abstime;
|
||||||
extern emu_timer *refresh_timer;
|
// extern emu_timer *refresh_timer;
|
||||||
scantime = timer_starttime(refresh_timer);
|
// scantime = timer_starttime(refresh_timer);
|
||||||
abstime = timer_get_time();
|
// abstime = timer_get_time();
|
||||||
while (attotime_compare(abstime, scantime) >= 0)
|
// while (attotime_compare(abstime, scantime) >= 0)
|
||||||
scantime = attotime_add(scantime, video_screen_get_frame_period(ccpu.scrnum));
|
// scantime = attotime_add(scantime, video_screen_get_frame_period(ccpu.scrnum));
|
||||||
cpu_spinuntil_time(attotime_sub(scantime, abstime));
|
// 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)); */
|
/* 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);
|
NEXT_ACC_A(); CYCLES(1);
|
||||||
|
@ -77,18 +77,17 @@ struct _cpuexec_data
|
|||||||
UINT8 nexteatcycles; /* pending value */
|
UINT8 nexteatcycles; /* pending value */
|
||||||
INT32 trigger; /* pending trigger to release a trigger suspension */
|
INT32 trigger; /* pending trigger to release a trigger suspension */
|
||||||
|
|
||||||
INT32 iloops; /* number of interrupts remaining this frame */
|
|
||||||
|
|
||||||
UINT64 totalcycles; /* total CPU cycles executed */
|
UINT64 totalcycles; /* total CPU cycles executed */
|
||||||
attotime localtime; /* local time, relative to the timer system's global time */
|
attotime localtime; /* local time, relative to the timer system's global time */
|
||||||
INT32 clock; /* current active clock */
|
INT32 clock; /* current active clock */
|
||||||
double clockscale; /* current active clock scale factor */
|
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 */
|
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 */
|
/* general CPU variables */
|
||||||
static cpuexec_data cpu[MAX_CPU];
|
static cpuexec_data cpu[MAX_CPU];
|
||||||
|
|
||||||
static UINT8 vblank;
|
|
||||||
static UINT32 current_frame;
|
static UINT32 current_frame;
|
||||||
|
|
||||||
static int cycles_running;
|
static int cycles_running;
|
||||||
@ -108,16 +106,6 @@ static int cycles_stolen;
|
|||||||
|
|
||||||
|
|
||||||
/* timer variables */
|
/* 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 emu_timer *timeslice_timer;
|
||||||
static attotime timeslice_period;
|
static attotime timeslice_period;
|
||||||
|
|
||||||
@ -139,14 +127,10 @@ static emu_timer *watchdog_timer;
|
|||||||
static void cpuexec_exit(running_machine *machine);
|
static void cpuexec_exit(running_machine *machine);
|
||||||
static void cpuexec_reset(running_machine *machine);
|
static void cpuexec_reset(running_machine *machine);
|
||||||
static void cpu_inittimers(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( end_interleave_boost );
|
||||||
|
static TIMER_CALLBACK( trigger_partial_frame_interrupt );
|
||||||
static void compute_perfect_interleave(running_machine *machine);
|
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)
|
void cpuexec_init(running_machine *machine)
|
||||||
{
|
{
|
||||||
int numscreens = video_screen_count(machine->config);
|
|
||||||
int cpunum;
|
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 */
|
/* loop over all our CPUs */
|
||||||
for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
|
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].clock);
|
||||||
state_save_register_item("cpu", cpunum, cpu[cpunum].clockscale);
|
state_save_register_item("cpu", cpunum, cpu[cpunum].clockscale);
|
||||||
|
|
||||||
state_save_register_item("cpu", cpunum, cpu[cpunum].vblankint_countdown);
|
|
||||||
|
|
||||||
/* initialize this CPU */
|
/* initialize this CPU */
|
||||||
state_save_push_tag(cpunum + 1);
|
state_save_push_tag(cpunum + 1);
|
||||||
num_regs = state_save_get_reg_count();
|
num_regs = state_save_get_reg_count();
|
||||||
@ -235,11 +206,9 @@ void cpuexec_init(running_machine *machine)
|
|||||||
|
|
||||||
/* save some stuff in the default tag */
|
/* save some stuff in the default tag */
|
||||||
state_save_push_tag(0);
|
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, current_frame);
|
||||||
state_save_register_item("cpu", 0, watchdog_enabled);
|
state_save_register_item("cpu", 0, watchdog_enabled);
|
||||||
state_save_register_item("cpu", 0, watchdog_counter);
|
state_save_register_item("cpu", 0, watchdog_counter);
|
||||||
state_save_register_item("cpu", 0, vblank_countdown);
|
|
||||||
state_save_pop_tag();
|
state_save_pop_tag();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,8 +247,6 @@ static void cpuexec_reset(running_machine *machine)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* reset the globals */
|
/* reset the globals */
|
||||||
cpu_vblankreset(machine);
|
|
||||||
vblank = 0;
|
|
||||||
current_frame = 0;
|
current_frame = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -857,33 +824,6 @@ void watchdog_enable(running_machine *machine, int enable)
|
|||||||
CHEESY FAKE VIDEO TIMING
|
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
|
cpu_scalebyfcount - scale by time between
|
||||||
refresh timers
|
refresh timers
|
||||||
@ -891,18 +831,19 @@ void cpu_compute_vblank_timing(running_machine *machine)
|
|||||||
|
|
||||||
int cpu_scalebyfcount(int value)
|
int cpu_scalebyfcount(int value)
|
||||||
{
|
{
|
||||||
attotime refresh_elapsed = timer_timeelapsed(refresh_timer);
|
// attotime refresh_elapsed = timer_timeelapsed(refresh_timer);
|
||||||
int result;
|
// int result;
|
||||||
|
|
||||||
/* shift off some bits to ensure no overflow */
|
/* shift off some bits to ensure no overflow */
|
||||||
if (value < 65536)
|
// if (value < 65536)
|
||||||
result = value * (refresh_elapsed.attoseconds >> 16) / (refresh_period.attoseconds >> 16);
|
// result = value * (refresh_elapsed.attoseconds >> 16) / (refresh_period.attoseconds >> 16);
|
||||||
else
|
// else
|
||||||
result = value * (refresh_elapsed.attoseconds >> 32) / (refresh_period.attoseconds >> 32);
|
// result = value * (refresh_elapsed.attoseconds >> 32) / (refresh_period.attoseconds >> 32);
|
||||||
if (value >= 0)
|
// if (value >= 0)
|
||||||
return (result < value) ? result : value;
|
// return (result < value) ? result : value;
|
||||||
else
|
// else
|
||||||
return (result > value) ? result : value;
|
// 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
|
cpu_getcurrentframe - return the current
|
||||||
frame count (deprecated)
|
frame count (deprecated)
|
||||||
@ -940,147 +870,121 @@ int cpu_getcurrentframe(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
INTERNAL TIMING
|
INTERNAL TIMING
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
/*-------------------------------------------------
|
/*-------------------------------------------------
|
||||||
cpu_vblankreset - hook for updating things on
|
on_global_vblank - performs VBLANK related
|
||||||
cheesy fake VBLANK (once per frame)
|
housekeeping
|
||||||
-------------------------------------------------*/
|
-------------------------------------------------*/
|
||||||
|
|
||||||
static void cpu_vblankreset(running_machine *machine)
|
static void on_global_vblank(running_machine *machine, screen_state *screen, int vblank_state)
|
||||||
|
{
|
||||||
|
/* VBLANK starting */
|
||||||
|
if (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 */
|
/* check the watchdog */
|
||||||
if (machine->config->watchdog_vblank_count != 0 && --watchdog_counter == 0)
|
if (machine->config->watchdog_vblank_count != 0 && --watchdog_counter == 0)
|
||||||
watchdog_callback(machine, NULL, 0);
|
watchdog_callback(machine, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* reset the cycle counters */
|
/* VBLANK ending - increment total frames */
|
||||||
for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
|
|
||||||
{
|
|
||||||
if (!(cpu[cpunum].suspend & SUSPEND_REASON_DISABLE))
|
|
||||||
cpu[cpunum].iloops = machine->config->cpu[cpunum].vblank_interrupts_per_frame - 1;
|
|
||||||
else
|
else
|
||||||
cpu[cpunum].iloops = -1;
|
current_frame++;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*-------------------------------------------------
|
/*-------------------------------------------------
|
||||||
cpu_firstvblankcallback - timer callback for
|
on_per_screen_vblank - calls any external
|
||||||
the first fake VBLANK
|
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 */
|
/* VBLANK starting */
|
||||||
timer_adjust_periodic(vblank_timer, vblank_period, param, vblank_period);
|
if (vblank_state)
|
||||||
|
|
||||||
/* 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;
|
int cpunum;
|
||||||
|
const char *screen_tag = NULL;
|
||||||
|
const device_config *device;
|
||||||
|
|
||||||
if (vblank_countdown == 1)
|
/* get the screen's tag */
|
||||||
vblank = 1;
|
for (device = video_screen_first(machine->config); device != NULL; device = video_screen_next(device))
|
||||||
|
if (device->token == screen)
|
||||||
|
screen_tag = device->tag;
|
||||||
|
|
||||||
/* loop over CPUs */
|
assert(screen_tag != NULL);
|
||||||
|
|
||||||
|
/* find any CPUs that have this screen as their VBLANK interrupt source */
|
||||||
for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
|
for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
|
||||||
{
|
{
|
||||||
/* if the interrupt multiplier is valid */
|
int cpu_interested;
|
||||||
if (cpu[cpunum].vblankint_multiplier != -1)
|
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)
|
||||||
{
|
{
|
||||||
/* decrement; if we hit zero, generate the interrupt and reset the countdown */
|
if (!cpunum_is_suspended(cpunum, SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE))
|
||||||
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);
|
cpuintrf_push_context(cpunum);
|
||||||
(*machine->config->cpu[cpunum].vblank_interrupt)(machine, cpunum);
|
(*machine->config->cpu[cpunum].vblank_interrupt)(machine, cpunum);
|
||||||
cpuintrf_pop_context();
|
cpuintrf_pop_context();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update the counters */
|
/* if we have more than one interrupt per frame, start the timer now to trigger the rest of them */
|
||||||
cpu[cpunum].iloops--;
|
if ((config->vblank_interrupts_per_frame > 1) &&
|
||||||
}
|
!(cpu[cpunum].suspend & SUSPEND_REASON_DISABLE))
|
||||||
|
|
||||||
/* reset the countdown and timer */
|
|
||||||
cpu[cpunum].vblankint_countdown = cpu[cpunum].vblankint_multiplier;
|
|
||||||
timer_adjust_oneshot(cpu[cpunum].vblankint_timer, attotime_never, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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? */
|
cpu[cpunum].partial_frame_period = attotime_div(video_screen_get_frame_period(0), config->vblank_interrupts_per_frame);
|
||||||
if (!(machine->config->video_attributes & VIDEO_UPDATE_AFTER_VBLANK))
|
timer_adjust_oneshot(cpu[cpunum].partial_frame_timer, cpu[cpunum].partial_frame_period, cpunum);
|
||||||
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
|
trigger_partial_frame_interrupt - called to
|
||||||
the fake end of VBLANK
|
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 */
|
int cpunum = param;
|
||||||
if (machine->config->video_attributes & VIDEO_UPDATE_AFTER_VBLANK)
|
|
||||||
video_frame_update(machine, FALSE);
|
|
||||||
vblank = 0;
|
|
||||||
|
|
||||||
/* update IPT_VBLANK input ports */
|
cpu[cpunum].iloops--;
|
||||||
input_port_vblank_end();
|
|
||||||
|
|
||||||
/* track total frames */
|
/* call the interrupt handler */
|
||||||
current_frame++;
|
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 */
|
/* more? */
|
||||||
timer_adjust_oneshot(refresh_timer, attotime_never, 0);
|
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);
|
int numscreens = video_screen_count(machine->config);
|
||||||
attoseconds_t refresh_attosecs;
|
attoseconds_t refresh_attosecs;
|
||||||
attotime first_time;
|
int cpunum, ipf;
|
||||||
int cpunum, max, ipf;
|
|
||||||
|
|
||||||
/* allocate a timer for the watchdog */
|
/* allocate a timer for the watchdog */
|
||||||
watchdog_timer = timer_alloc(watchdog_callback, NULL);
|
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 = timer_alloc(NULL, NULL);
|
||||||
interleave_boost_timer_end = timer_alloc(end_interleave_boost, NULL);
|
interleave_boost_timer_end = timer_alloc(end_interleave_boost, NULL);
|
||||||
|
|
||||||
/*
|
/* register the screen specific VBLANK callbacks */
|
||||||
* 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;
|
|
||||||
for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
|
for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
|
||||||
{
|
{
|
||||||
ipf = machine->config->cpu[cpunum].vblank_interrupts_per_frame;
|
const cpu_config *config = machine->config->cpu + cpunum;
|
||||||
if (ipf > max)
|
|
||||||
max = ipf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* now find the LCD with the rest of the CPUs (brute force - these numbers aren't huge) */
|
if (config->vblank_interrupts_per_frame > 0)
|
||||||
vblank_multiplier = max;
|
|
||||||
while (1)
|
|
||||||
{
|
{
|
||||||
for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
|
const char *screen_tag;
|
||||||
{
|
screen_state *screen;
|
||||||
ipf = machine->config->cpu[cpunum].vblank_interrupts_per_frame;
|
|
||||||
if (ipf > 0 && (vblank_multiplier % ipf) != 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (cpunum == cpu_gettotalcpu())
|
|
||||||
break;
|
|
||||||
vblank_multiplier += max;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialize the countdown timers and intervals */
|
/* get the screen that will trigger the VBLANK */
|
||||||
for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
|
|
||||||
{
|
/* new style - use screen tag directly */
|
||||||
ipf = machine->config->cpu[cpunum].vblank_interrupts_per_frame;
|
if (config->vblank_interrupts_per_frame == 1)
|
||||||
if (ipf > 0)
|
screen_tag = config->vblank_interrupt_screen;
|
||||||
cpu[cpunum].vblankint_countdown = cpu[cpunum].vblankint_multiplier = vblank_multiplier / ipf;
|
|
||||||
|
/* old style 'hack' setup - use screen #0 */
|
||||||
else
|
else
|
||||||
cpu[cpunum].vblankint_countdown = cpu[cpunum].vblankint_multiplier = -1;
|
{
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* allocate a vblank timer at the frame rate * the LCD number of interrupts per frame */
|
screen = devtag_get_token(machine, VIDEO_SCREEN, screen_tag);
|
||||||
vblank_period = attotime_make(0, refresh_attosecs / vblank_multiplier);
|
video_screen_register_vbl_cb(machine, screen, on_per_screen_vblank);
|
||||||
vblank_countdown = vblank_multiplier;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* allocate an update timer that will be used to time the actual screen updates */
|
/* register the global VBLANK callback -- it's important to do it after the
|
||||||
update_timer = timer_alloc(cpu_updatecallback, NULL);
|
screen specific ones */
|
||||||
|
video_screen_register_vbl_cb(machine, NULL, on_global_vblank);
|
||||||
|
|
||||||
/*
|
/* start the CPU periodic interrupt timers */
|
||||||
* 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++)
|
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 */
|
/* see if we need to allocate a CPU timer */
|
||||||
if (machine->config->cpu[cpunum].timed_interrupt_period != 0)
|
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);
|
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);
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
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);
|
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
|
debug_vblank_hook - called when an interrupt
|
||||||
is acknowledged
|
is acknowledged
|
||||||
|
@ -182,7 +182,6 @@ void debug_source_script(const char *file);
|
|||||||
void debug_flush_traces(void);
|
void debug_flush_traces(void);
|
||||||
|
|
||||||
/* debugging hooks */
|
/* debugging hooks */
|
||||||
void debug_vblank_hook(void);
|
|
||||||
void debug_interrupt_hook(int cpunum, int irqline);
|
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_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));
|
void debug_set_instruction_hook(int cpunum, int (*hook)(offs_t pc));
|
||||||
|
@ -1034,11 +1034,14 @@ static int default_ports_lookup[__ipt_max][MAX_PLAYERS];
|
|||||||
FUNCTION PROTOTYPES
|
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_playback(running_machine *machine);
|
||||||
static void setup_record(running_machine *machine);
|
static void setup_record(running_machine *machine);
|
||||||
static void input_port_exit(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_load(int config_type, xml_data_node *parentnode);
|
||||||
static void input_port_save(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_digital_joysticks(void);
|
||||||
static void update_analog_port(int port);
|
static void update_analog_port(int port);
|
||||||
static void interpolate_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
|
* 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 ui_visible = ui_is_menu_active() || ui_is_slider_active();
|
||||||
int portnum, bitnum;
|
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 ui_visible = ui_is_menu_active() || ui_is_slider_active();
|
||||||
int port;
|
int port;
|
||||||
@ -3417,19 +3447,9 @@ UINT32 readinputport(int port)
|
|||||||
result = (result & ~portinfo->vblank) | (portinfo->defvalue & portinfo->vblank);
|
result = (result & ~portinfo->vblank) | (portinfo->defvalue & portinfo->vblank);
|
||||||
|
|
||||||
/* toggle VBLANK if we're in a VBLANK state */
|
/* 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))
|
if (video_screen_get_vblank(0))
|
||||||
result ^= portinfo->vblank;
|
result ^= portinfo->vblank;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -893,6 +893,7 @@ struct _ext_inp_header
|
|||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
void input_port_init(running_machine *machine, const input_port_token *ipt);
|
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);
|
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);
|
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);
|
int input_ui_pressed_repeat(int code, int speed);
|
||||||
|
|
||||||
void input_port_update_defaults(void);
|
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);
|
void input_port_set_digital_value(int port, UINT32 value, UINT32 mask);
|
||||||
|
|
||||||
|
@ -1573,12 +1573,6 @@ static void init_machine(running_machine *machine)
|
|||||||
devices_init(machine);
|
devices_init(machine);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_DEBUGGER
|
|
||||||
/* initialize the debugger */
|
|
||||||
if (machine->debug_mode)
|
|
||||||
mame_debug_init(machine);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* call the game driver's init function */
|
/* call the game driver's init function */
|
||||||
/* this is where decryption is done and memory maps are altered */
|
/* this is where decryption is done and memory maps are altered */
|
||||||
/* so this location in the init order is important */
|
/* so this location in the init order is important */
|
||||||
@ -1593,6 +1587,13 @@ static void init_machine(running_machine *machine)
|
|||||||
device_list_start(machine);
|
device_list_start(machine);
|
||||||
|
|
||||||
sound_init(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 */
|
/* call the driver's _START callbacks */
|
||||||
if (machine->config->machine_start != NULL) (*machine->config->machine_start)(machine);
|
if (machine->config->machine_start != NULL) (*machine->config->machine_start)(machine);
|
||||||
|
@ -780,27 +780,13 @@ 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);
|
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;
|
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);
|
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;
|
error = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (cpu->vblank_interrupts_per_frame != 0)
|
else if (cpu->vblank_interrupts_per_frame != 0)
|
||||||
{
|
{
|
||||||
mame_printf_error("%s: %s cpu #%d has no VBLANK interrupt handler but a non-0 interrupt count is given!\n", driver->source_file, driver->name, cpunum);
|
mame_printf_error("%s: %s cpu #%d has no VBLANK interrupt handler but a non-0 interrupt count is given!\n", driver->source_file, driver->name, cpunum);
|
||||||
|
194
src/emu/video.c
194
src/emu/video.c
@ -39,7 +39,8 @@
|
|||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
#define SUBSECONDS_PER_SPEED_UPDATE (ATTOSECONDS_PER_SECOND / 4)
|
#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,6 +52,7 @@ typedef struct _internal_screen_info internal_screen_info;
|
|||||||
struct _internal_screen_info
|
struct _internal_screen_info
|
||||||
{
|
{
|
||||||
/* pointers to screen configuration and state */
|
/* pointers to screen configuration and state */
|
||||||
|
int scrnum; /* the screen index */
|
||||||
const screen_config * config; /* pointer to the configuration in the Machine->config */
|
const screen_config * config; /* pointer to the configuration in the Machine->config */
|
||||||
screen_state * state; /* pointer to visible state in Machine structure */
|
screen_state * state; /* pointer to visible state in Machine structure */
|
||||||
|
|
||||||
@ -62,14 +64,21 @@ struct _internal_screen_info
|
|||||||
bitmap_format format; /* format of bitmap for this screen */
|
bitmap_format format; /* format of bitmap for this screen */
|
||||||
UINT8 changed; /* has this bitmap changed? */
|
UINT8 changed; /* has this bitmap changed? */
|
||||||
INT32 last_partial_scan; /* scanline of last partial update */
|
INT32 last_partial_scan; /* scanline of last partial update */
|
||||||
|
UINT8 vblank_state; /* 1 = in VBLANK region, 0 = outside */
|
||||||
|
|
||||||
/* screen timing */
|
/* screen timing */
|
||||||
attoseconds_t scantime; /* attoseconds per scanline */
|
attoseconds_t scantime; /* attoseconds per scanline */
|
||||||
attoseconds_t pixeltime; /* attoseconds per pixel */
|
attoseconds_t pixeltime; /* attoseconds per pixel */
|
||||||
attotime vblank_time; /* time of last VBLANK start */
|
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 * scanline0_timer; /* scanline 0 timer */
|
||||||
emu_timer * scanline_timer; /* scanline 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 */
|
/* movie recording */
|
||||||
mame_file * movie_file; /* handle to the open movie file */
|
mame_file * movie_file; /* handle to the open movie file */
|
||||||
UINT32 movie_frame; /* current movie frame number */
|
UINT32 movie_frame; /* current movie frame number */
|
||||||
@ -79,7 +88,7 @@ struct _internal_screen_info
|
|||||||
struct _video_private
|
struct _video_private
|
||||||
{
|
{
|
||||||
/* per-screen information */
|
/* per-screen information */
|
||||||
internal_screen_info scrinfo[MAX_SCREENS]; /* per-screen infomration */
|
internal_screen_info scrinfo[MAX_SCREENS]; /* per-screen information */
|
||||||
|
|
||||||
/* snapshot stuff */
|
/* snapshot stuff */
|
||||||
render_target * snap_target; /* screen shapshot target */
|
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);
|
static void decode_graphics(running_machine *machine, const gfx_decode_entry *gfxdecodeinfo);
|
||||||
|
|
||||||
/* global rendering */
|
/* global rendering */
|
||||||
|
static TIMER_CALLBACK( vblank_begin_callback );
|
||||||
|
static TIMER_CALLBACK( vblank_end_callback );
|
||||||
static TIMER_CALLBACK( scanline0_callback );
|
static TIMER_CALLBACK( scanline0_callback );
|
||||||
static TIMER_CALLBACK( scanline_update_callback );
|
static TIMER_CALLBACK( scanline_update_callback );
|
||||||
static int finish_screen_updates(running_machine *machine);
|
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);
|
render_container *container = render_container_get_screen(scrnum);
|
||||||
internal_screen_info *info = &viddata->scrinfo[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 */
|
/* allocate a timer to reset partial updates */
|
||||||
info->scanline0_timer = timer_alloc(scanline0_callback, NULL);
|
info->scanline0_timer = timer_alloc(scanline0_callback, NULL);
|
||||||
|
|
||||||
/* make pointers back to the config and state */
|
/* make pointers back to the config and state */
|
||||||
|
info->scrnum = scrnum;
|
||||||
info->config = device->inline_config;
|
info->config = device->inline_config;
|
||||||
info->state = &machine->screen[scrnum];
|
info->state = &machine->screen[scrnum];
|
||||||
|
|
||||||
@ -326,7 +342,7 @@ void video_init(running_machine *machine)
|
|||||||
render_container_set_yscale(container, info->config->yscale);
|
render_container_set_yscale(container, info->config->yscale);
|
||||||
|
|
||||||
/* reset VBLANK timing */
|
/* 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 */
|
/* allocate a timer to generate per-scanline updates */
|
||||||
if (machine->config->video_attributes & VIDEO_UPDATE_SCANLINE)
|
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
|
init_buffered_spriteram - initialize the
|
||||||
double-buffered spriteram
|
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 */
|
/* if we are on scanline 0 already, reset the update timer immediately */
|
||||||
/* otherwise, defer until the next scanline 0 */
|
/* otherwise, defer until the next scanline 0 */
|
||||||
if (video_screen_get_vpos(scrnum) == 0)
|
if (video_screen_get_vpos(scrnum) == 0)
|
||||||
timer_adjust_oneshot(info->scanline0_timer, attotime_zero, scrnum);
|
timer_adjust_oneshot(info->scanline0_timer, attotime_zero, scrnum);
|
||||||
else
|
else
|
||||||
timer_adjust_oneshot(info->scanline0_timer, video_screen_get_time_until_pos(scrnum, 0, 0), scrnum);
|
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)
|
int video_screen_get_vblank(int scrnum)
|
||||||
{
|
{
|
||||||
internal_screen_info *info = get_screen_info(Machine, scrnum);
|
internal_screen_info *info = get_screen_info(Machine, scrnum);
|
||||||
int vpos = video_screen_get_vpos(scrnum);
|
return info->vblank_state;
|
||||||
return (vpos < info->state->visarea.min_y || vpos > info->state->visarea.max_y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -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
|
video_screen_get_scan_period - return the
|
||||||
amount of time the beam takes to draw one
|
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
|
VIDEO SCREEN DEVICE INTERFACE
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
@ -1111,6 +1171,74 @@ DEVICE_GET_INFO( video_screen )
|
|||||||
GLOBAL RENDERING
|
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
|
scanline0_callback - reset partial updates
|
||||||
for a screen
|
for a screen
|
||||||
|
@ -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
|
FUNCTION PROTOTYPES
|
||||||
@ -98,9 +107,6 @@ struct _screen_config
|
|||||||
/* core initialization */
|
/* core initialization */
|
||||||
void video_init(running_machine *machine);
|
void video_init(running_machine *machine);
|
||||||
|
|
||||||
/* core VBLANK callback */
|
|
||||||
void video_vblank_start(running_machine *machine);
|
|
||||||
|
|
||||||
|
|
||||||
/* ----- screen management ----- */
|
/* ----- 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 */
|
/* 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);
|
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 */
|
/* return the amount of time the beam takes to draw one scan line */
|
||||||
attotime video_screen_get_scan_period(int scrnum);
|
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 */
|
/* returns whether a given screen exists */
|
||||||
int video_screen_exists(int scrnum);
|
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 ----- */
|
/* ----- video screen device interface ----- */
|
||||||
|
Loading…
Reference in New Issue
Block a user