Implements scanline based timer devices.

Converted Centipede, as an example.

To define a scanline timer, use something like this:

MDRV_TIMER_ADD("32V", SCANLINE, generate_interrupt)
MDRV_TIMER_SCANLINE("main", 0, 16)

The first number is the first scanline the timer will fire on, the 2nd number is the increment.
So in this case, the timer will fire on 0, 16, 32, ..., 224, 240, then wrap around
because the screen is defined as 256 lines high.
The current scanline is passed to the callback in its 'param' argument
This commit is contained in:
Zsolt Vasvari 2008-03-14 11:15:31 +00:00
parent e42b02f565
commit e4a07ea688
4 changed files with 154 additions and 41 deletions

View File

@ -466,6 +466,11 @@ union _machine_config_token
#define MDRV_TIMER_PTR(_ptr) \
MDRV_DEVICE_CONFIG_DATAPTR(timer_config, ptr, _ptr)
#define MDRV_TIMER_SCANLINE(_screen, _first_vpos, _increment) \
MDRV_DEVICE_CONFIG_DATAPTR(timer_config, screen, _screen) \
MDRV_DEVICE_CONFIG_DATA32(timer_config, first_vpos, _first_vpos) \
MDRV_DEVICE_CONFIG_DATA32(timer_config, increment, _increment)
/* core sound functions */
#define MDRV_SOUND_START(_func) \

View File

@ -66,10 +66,15 @@ typedef struct _timer_state timer_state;
struct _timer_state
{
emu_timer *timer; /* the backing timer */
void *ptr; /* the pointer parameter passed to the timer callback */
/* periodic timers only */
attotime duration; /* duration before the timer fires */
attotime period; /* period of repeated timer firings */
INT32 param; /* the integer parameter passed to the timer callback */
void *ptr; /* the pointer parameter passed to the timer callback */
/* scanline timers only */
UINT32 first_time; /* indicates that the system is starting */
};
@ -568,6 +573,11 @@ void timer_adjust_oneshot(emu_timer *which, attotime duration, INT32 param)
void timer_device_adjust_oneshot(const device_config *timer, attotime duration, INT32 param)
{
timer_config *config = timer->inline_config;
/* only makes sense for periodic timers */
assert(config->type == TIMER_TYPE_PERIODIC);
timer_device_adjust_periodic(timer, duration, param, attotime_never);
}
@ -613,6 +623,10 @@ void timer_adjust_periodic(emu_timer *which, attotime duration, INT32 param, att
void timer_device_adjust_periodic(const device_config *timer, attotime duration, INT32 param, attotime period)
{
timer_state *state = get_safe_token(timer);
timer_config *config = timer->inline_config;
/* only makes sense for periodic timers */
assert(config->type == TIMER_TYPE_PERIODIC);
state->duration = duration;
state->period = period;
@ -671,6 +685,11 @@ void timer_reset(emu_timer *which, attotime duration)
void timer_device_reset(const device_config *timer, attotime duration)
{
timer_state *state = get_safe_token(timer);
timer_config *config = timer->inline_config;
/* only makes sense for periodic timers */
assert(config->type == TIMER_TYPE_PERIODIC);
timer_adjust_periodic(state->timer, state->duration, 0, state->period);
}
@ -735,6 +754,11 @@ int timer_get_param(emu_timer *which)
int timer_device_get_param(const device_config *timer)
{
timer_state *state = get_safe_token(timer);
timer_config *config = timer->inline_config;
/* only makes sense for periodic timers */
assert(config->type == TIMER_TYPE_PERIODIC);
return state->param;
}
@ -839,18 +863,68 @@ attotime timer_device_firetime(const device_config *timer)
/*-------------------------------------------------
timer_device_timer_callback - calls
periodic_timer_device_timer_callback - calls
the timer device specific callback
-------------------------------------------------*/
static TIMER_CALLBACK( timer_device_timer_callback )
static TIMER_CALLBACK( periodic_timer_device_timer_callback )
{
const device_config *timer_device = ptr;
timer_state *state = get_safe_token(timer_device);
timer_config *config = timer_device->inline_config;
const device_config *timer = ptr;
timer_state *state = get_safe_token(timer);
timer_config *config = timer->inline_config;
/* call the real callback */
config->callback(timer_device, state->ptr, state->param);
config->callback(timer, state->ptr, state->param);
}
/*-------------------------------------------------
scanline_timer_device_timer_callback -
manages the scanline based timer's state
-------------------------------------------------*/
static TIMER_CALLBACK( scanline_timer_device_timer_callback )
{
int next_vpos;
const device_config *timer = ptr;
timer_state *state = get_safe_token(timer);
timer_config *config = timer->inline_config;
/* get the screen device and verify it */
const device_config *screen = device_list_find_by_tag(timer->machine->config->devicelist, VIDEO_SCREEN, config->screen);
assert(screen != NULL);
/* first time, start with the first scanline, but do not call the callback */
if (state->first_time)
{
next_vpos = config->first_vpos;
/* indicate that we are done with the first call */
state->first_time = FALSE;
}
/* not the first time */
else
{
int vpos = video_screen_get_vpos(screen);
/* call the real callback */
config->callback(timer, state->ptr, vpos);
/* if the increment is 0 or the next scanline is larger than the screen size,
go back to the first one */
if ((config->increment == 0) ||
((vpos + config->increment) >= video_screen_get_height(screen)))
next_vpos = config->first_vpos;
/* otherwise, increment */
else
next_vpos = vpos + config->increment;
}
/* adjust the timer */
timer_adjust_oneshot(state->timer, video_screen_get_time_until_pos(screen, next_vpos, 0), 0);
}
@ -899,7 +973,7 @@ static void timer_logtimers(void)
static DEVICE_START( timer )
{
char unique_tag[40];
char unique_tag[50];
timer_state *state;
timer_config *config;
void *param;
@ -913,28 +987,35 @@ static DEVICE_START( timer )
/* get and validate the configuration */
config = device->inline_config;
assert(config->type == TIMER_TYPE_PERIODIC);
assert((config->type == TIMER_TYPE_PERIODIC) || (config->type == TIMER_TYPE_SCANLINE));
assert(config->callback != NULL);
/* everything checks out so far, allocate the state object */
state = auto_malloc(sizeof(*state));
memset(state, 0, sizeof(*state));
/* allocate the backing timer */
param = (void *)device;
state->timer = timer_alloc(timer_device_timer_callback, param);
/* copy the parameters */
state->param = config->param;
/* copy the pointer parameter */
state->ptr = config->ptr;
/* switch on the type */
/* create the name for save states */
assert(strlen(device->tag) < 30);
state_save_combine_module_and_tag(unique_tag, "timer_device", device->tag);
/* type based configuration */
switch (config->type)
{
case TIMER_TYPE_PERIODIC:
/* make sure that only the applicable parameters are filled in */
assert(config->screen == NULL);
assert(config->first_vpos == 0);
assert(config->increment == 0);
/* validate that we have at least a duration or period */
assert((config->duration > 0) || (config->period > 0));
/* copy the optional integer parameter */
state->param = config->param;
/* convert the duration and period into attotime */
if (config->duration > 0)
state->duration = UINT64_ATTOTIME_TO_ATTOTIME(config->duration);
@ -946,24 +1027,50 @@ static DEVICE_START( timer )
else
state->period = attotime_never;
/* register for state saves */
state_save_register_item(unique_tag, 0, state->duration.seconds);
state_save_register_item(unique_tag, 0, state->duration.attoseconds);
state_save_register_item(unique_tag, 0, state->period.seconds);
state_save_register_item(unique_tag, 0, state->period.attoseconds);
state_save_register_item(unique_tag, 0, state->param);
/* allocate the backing timer */
param = (void *)device;
state->timer = timer_alloc(periodic_timer_device_timer_callback, param);
/* finally, start the timer */
timer_adjust_periodic(state->timer, state->duration, 0, state->period);
break;
case TIMER_TYPE_SCANLINE:
/* make sure that only the applicable parameters are filled in */
assert(config->duration == 0);
assert(config->period == 0);
assert(config->param == 0);
assert(config->first_vpos >= 0);
assert(config->increment >= 0);
/* allocate the backing timer */
param = (void *)device;
state->timer = timer_alloc(scanline_timer_device_timer_callback, param);
/* indicate that this will be the first call */
state->first_time = TRUE;
/* register for state saves */
state_save_register_item(unique_tag, 0, state->first_time);
/* fire it as soon as the emulation starts */
timer_adjust_oneshot(state->timer, attotime_zero, 0);
break;
default:
/* we will never get here */
fatalerror("Unknown timer device type");
break;
}
/* register for save states */
assert(strlen(device->tag) < 30);
state_save_combine_module_and_tag(unique_tag, "timer_device", device->tag);
state_save_register_item(unique_tag, 0, state->duration.seconds);
state_save_register_item(unique_tag, 0, state->duration.attoseconds);
state_save_register_item(unique_tag, 0, state->period.seconds);
state_save_register_item(unique_tag, 0, state->period.attoseconds);
state_save_register_item(unique_tag, 0, state->param);
return state;
}

View File

@ -27,7 +27,8 @@
/* timer types */
enum
{
TIMER_TYPE_PERIODIC = 0
TIMER_TYPE_PERIODIC = 0,
TIMER_TYPE_SCANLINE
};
@ -81,10 +82,17 @@ struct _timer_config
{
int type; /* type of timer */
timer_device_fired_func callback; /* the timer's callback function */
void *ptr; /* the pointer parameter passed to the timer callback */
/* periodic timers only */
UINT64 duration; /* duration before the timer fires */
UINT64 period; /* period of repeated timer firings */
INT32 param; /* the integer parameter passed to the timer callback */
void *ptr; /* the pointer parameter passed to the timer callback */
/* scanline timers only */
const char *screen; /* the name of the screen this timer tracks */
UINT32 first_vpos; /* the first vertical scanline position the timer fires on */
UINT32 increment; /* the number of scanlines between firings */
};

View File

@ -431,8 +431,6 @@ static UINT8 sign[4];
static UINT8 dsw_select, control_select;
static UINT8 *rambase;
static emu_timer *interrupt_timer;
/*************************************
*
@ -440,23 +438,16 @@ static emu_timer *interrupt_timer;
*
*************************************/
static TIMER_CALLBACK( generate_interrupt )
static TIMER_DEVICE_CALLBACK( generate_interrupt )
{
int scanline = param;
/* IRQ is clocked on the rising edge of 16V, equal to the previous 32V */
if (scanline & 16)
cpunum_set_input_line(machine, 0, 0, ((scanline - 1) & 32) ? ASSERT_LINE : CLEAR_LINE);
cpunum_set_input_line(timer->machine, 0, 0, ((scanline - 1) & 32) ? ASSERT_LINE : CLEAR_LINE);
/* do a partial update now to handle sprite multiplexing (Maze Invaders) */
video_screen_update_partial(machine->primary_screen, scanline);
/* call back again after 16 scanlines */
scanline += 16;
if (scanline >= 256)
scanline = 0;
timer_adjust_oneshot(interrupt_timer, video_screen_get_time_until_pos(machine->primary_screen, scanline, 0), scanline);
video_screen_update_partial(timer->machine->primary_screen, scanline);
}
@ -470,8 +461,6 @@ static MACHINE_START( centiped )
static MACHINE_RESET( centiped )
{
interrupt_timer = timer_alloc(generate_interrupt, NULL);
timer_adjust_oneshot(interrupt_timer, video_screen_get_time_until_pos(machine->primary_screen, 0, 0), 0);
cpunum_set_input_line(machine, 0, 0, CLEAR_LINE);
dsw_select = 0;
control_select = 0;
@ -1635,6 +1624,10 @@ static MACHINE_DRIVER_START( centiped )
MDRV_MACHINE_RESET(centiped)
MDRV_NVRAM_HANDLER(atari_vg)
/* timer */
MDRV_TIMER_ADD("32V", SCANLINE, generate_interrupt)
MDRV_TIMER_SCANLINE("main", 0, 16)
/* video hardware */
MDRV_SCREEN_ADD("main", RASTER)
MDRV_SCREEN_REFRESH_RATE(60)