summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/video.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/video.c')
-rw-r--r--src/emu/video.c2211
1 files changed, 2211 insertions, 0 deletions
diff --git a/src/emu/video.c b/src/emu/video.c
new file mode 100644
index 00000000000..d7c98cda799
--- /dev/null
+++ b/src/emu/video.c
@@ -0,0 +1,2211 @@
+/***************************************************************************
+
+ video.c
+
+ Core MAME video routines.
+
+ Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+***************************************************************************/
+
+#include "osdepend.h"
+#include "driver.h"
+#include "profiler.h"
+#include "png.h"
+#include "debugger.h"
+#include "video/vector.h"
+#include "render.h"
+#include "rendutil.h"
+#include "ui.h"
+
+#include "snap.lh"
+
+
+
+/***************************************************************************
+ DEBUGGING
+***************************************************************************/
+
+#define LOG_THROTTLE (0)
+#define LOG_PARTIAL_UPDATES(x) /* logerror x */
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define SUBSECONDS_PER_SPEED_UPDATE (ATTOSECONDS_PER_SECOND / 4)
+#define PAUSED_REFRESH_RATE 30
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+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->drv */
+ screen_state * state; /* pointer to visible state in Machine structure */
+
+ /* textures and bitmaps */
+ render_texture * texture[2]; /* 2x textures for the screen bitmap */
+ mame_bitmap * 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 */
+
+ /* 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 */
+
+ /* movie recording */
+ 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 */
+
+ /* snapshot stuff */
+ render_target * snap_target; /* screen shapshot target */
+ mame_bitmap * snap_bitmap; /* screen snapshot bitmap */
+
+ /* crosshair bits */
+ mame_bitmap * crosshair_bitmap[MAX_PLAYERS]; /* crosshair bitmap per player */
+ render_texture * crosshair_texture[MAX_PLAYERS]; /* crosshair texture per player */
+ UINT8 crosshair_animate; /* animation frame index */
+ UINT8 crosshair_visible; /* crosshair visible mask */
+ UINT8 crosshair_needed; /* crosshair needed mask */
+};
+
+
+typedef struct _video_global video_global;
+struct _video_global
+{
+ /* throttling calculations */
+ osd_ticks_t throttle_last_ticks;/* osd_ticks the last call to throttle */
+ attotime throttle_realtime; /* real time the last call to throttle */
+ attotime throttle_emutime; /* emulated time the last call to throttle */
+ UINT32 throttle_history; /* history of frames where we were fast enough */
+
+ /* dynamic speed computation */
+ osd_ticks_t speed_last_realtime;/* real time at the last speed calculation */
+ attotime speed_last_emutime; /* emulated time at the last speed calculation */
+ double speed_percent; /* most recent speed percentage */
+ UINT32 partial_updates_this_frame;/* partial update counter this frame */
+
+ /* overall speed computation */
+ UINT32 overall_real_seconds;/* accumulated real seconds at normal speed */
+ osd_ticks_t overall_real_ticks; /* accumulated real ticks at normal speed */
+ attotime overall_emutime; /* accumulated emulated time at normal speed */
+ UINT32 overall_valid_counter;/* number of consecutive valid time periods */
+
+ /* configuration */
+ UINT8 sleep; /* flag: TRUE if we're allowed to sleep */
+ UINT8 throttle; /* flag: TRUE if we're currently throttled */
+ UINT8 fastforward; /* flag: TRUE if we're currently fast-forwarding */
+ UINT32 seconds_to_run; /* number of seconds to run before quitting */
+ UINT8 auto_frameskip; /* flag: TRUE if we're automatically frameskipping */
+ UINT32 speed; /* overall speed (*100) */
+ UINT32 original_speed; /* originally-specified speed */
+ UINT8 refresh_speed; /* flag: TRUE if we max out our speed according to the refresh */
+
+ /* frameskipping */
+ UINT8 empty_skip_count; /* number of empty frames we have skipped */
+ UINT8 frameskip_level; /* current frameskip level */
+ UINT8 frameskip_counter; /* counter that counts through the frameskip steps */
+ INT8 frameskip_adjust;
+ UINT8 skipping_this_frame;/* flag: TRUE if we are skipping the current frame */
+ osd_ticks_t average_oversleep; /* average number of ticks the OSD oversleeps */
+};
+
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+/* global video state */
+static video_global global;
+
+/* frameskipping tables */
+static const UINT8 skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS] =
+{
+ { 0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,1 },
+ { 0,0,0,0,0,1,0,0,0,0,0,1 },
+ { 0,0,0,1,0,0,0,1,0,0,0,1 },
+ { 0,0,1,0,0,1,0,0,1,0,0,1 },
+ { 0,1,0,0,1,0,1,0,0,1,0,1 },
+ { 0,1,0,1,0,1,0,1,0,1,0,1 },
+ { 0,1,0,1,1,0,1,0,1,1,0,1 },
+ { 0,1,1,0,1,1,0,1,1,0,1,1 },
+ { 0,1,1,1,0,1,1,1,0,1,1,1 },
+ { 0,1,1,1,1,1,0,1,1,1,1,1 },
+ { 0,1,1,1,1,1,1,1,1,1,1,1 }
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* core implementation */
+static void video_exit(running_machine *machine);
+static void init_buffered_spriteram(void);
+
+/* graphics decoding */
+static void allocate_graphics(running_machine *machine, const gfx_decode_entry *gfxdecodeinfo);
+static void decode_graphics(running_machine *machine, const gfx_decode_entry *gfxdecodeinfo);
+
+/* global rendering */
+static TIMER_CALLBACK( scanline0_callback );
+static int finish_screen_updates(running_machine *machine);
+
+/* throttling/frameskipping/performance */
+static void update_throttle(attotime emutime);
+static osd_ticks_t throttle_until_ticks(osd_ticks_t target_ticks);
+static void update_frameskip(void);
+static void recompute_speed(attotime emutime);
+
+/* screen snapshots */
+static mame_bitmap *get_snapshot_bitmap(running_machine *machine, int scrnum);
+static file_error mame_fopen_next(const char *pathoption, const char *extension, mame_file **file);
+
+/* movie recording */
+static void movie_record_frame(running_machine *machine, int scrnum);
+
+/* crosshair rendering */
+static void crosshair_init(video_private *viddata);
+static void crosshair_render(video_private *viddata);
+static void crosshair_free(video_private *viddata);
+
+/* software rendering */
+static void rgb888_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch);
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ effective_autoframeskip - return the effective
+ autoframeskip value, accounting for fast
+ forward
+-------------------------------------------------*/
+
+INLINE int effective_autoframeskip(void)
+{
+ /* if we're fast forwarding or paused, autoframeskip is disabled */
+ if (global.fastforward || mame_is_paused(Machine))
+ return FALSE;
+
+ /* otherwise, it's up to the user */
+ return global.auto_frameskip;
+}
+
+
+/*-------------------------------------------------
+ effective_frameskip - return the effective
+ frameskip value, accounting for fast
+ forward
+-------------------------------------------------*/
+
+INLINE int effective_frameskip(void)
+{
+ /* if we're fast forwarding, use the maximum frameskip */
+ if (global.fastforward)
+ return FRAMESKIP_LEVELS - 1;
+
+ /* otherwise, it's up to the user */
+ return global.frameskip_level;
+}
+
+
+/*-------------------------------------------------
+ effective_throttle - return the effective
+ throttle value, accounting for fast
+ forward and user interface
+-------------------------------------------------*/
+
+INLINE int effective_throttle(void)
+{
+ /* if we're paused, or if the UI is active, we always throttle */
+ if (mame_is_paused(Machine) || ui_is_menu_active() || ui_is_slider_active())
+ return TRUE;
+
+ /* if we're fast forwarding, we don't throttle */
+ if (global.fastforward)
+ return FALSE;
+
+ /* otherwise, it's up to the user */
+ return global.throttle;
+}
+
+
+
+/***************************************************************************
+ CORE IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ video_init - start up the video system
+-------------------------------------------------*/
+
+void video_init(running_machine *machine)
+{
+ video_private *viddata;
+ const char *filename;
+ int scrnum;
+
+ /* reset our global state */
+ memset(&global, 0, sizeof(global));
+ global.speed_percent = 1.0;
+
+ /* extract global configuration settings */
+ global.sleep = options_get_bool(mame_options(), OPTION_SLEEP);
+ global.throttle = options_get_bool(mame_options(), OPTION_THROTTLE);
+ global.auto_frameskip = options_get_bool(mame_options(), OPTION_AUTOFRAMESKIP);
+ global.frameskip_level = options_get_int(mame_options(), OPTION_FRAMESKIP);
+ global.seconds_to_run = options_get_int(mame_options(), OPTION_SECONDS_TO_RUN);
+ global.original_speed = global.speed = (options_get_float(mame_options(), OPTION_SPEED) * 100.0 + 0.5);
+ global.refresh_speed = options_get_bool(mame_options(), OPTION_REFRESHSPEED);
+
+ /* allocate memory for our private data */
+ viddata = machine->video_data = auto_malloc(sizeof(*viddata));
+ memset(viddata, 0, sizeof(*viddata));
+
+ /* request a callback upon exiting */
+ add_exit_callback(machine, video_exit);
+
+ /* configure all of the screens */
+ for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++)
+ if (machine->drv->screen[scrnum].tag != NULL)
+ {
+ render_container *container = render_container_get_screen(scrnum);
+ internal_screen_info *info = &viddata->scrinfo[scrnum];
+
+ /* allocate a timer to reset partial updates */
+ info->scanline0_timer = timer_alloc(scanline0_callback);
+
+ /* make pointers back to the config and state */
+ info->config = &machine->drv->screen[scrnum];
+ info->state = &machine->screen[scrnum];
+
+ /* configure the screen with the default parameters */
+ video_screen_configure(scrnum, info->state->width, info->state->height, &info->state->visarea, info->state->refresh);
+
+ /* configure the default cliparea */
+ if (info->config->xoffset != 0)
+ render_container_set_xoffset(container, info->config->xoffset);
+ if (info->config->yoffset != 0)
+ render_container_set_yoffset(container, info->config->yoffset);
+ if (info->config->xscale != 0)
+ render_container_set_xscale(container, info->config->xscale);
+ if (info->config->yscale != 0)
+ render_container_set_yscale(container, info->config->yscale);
+
+ /* reset VBLANK timing */
+ info->vblank_time = attotime_sub_attoseconds(attotime_zero, machine->screen[0].vblank);
+
+ /* register for save states */
+ state_save_register_item("video", scrnum, info->vblank_time.seconds);
+ state_save_register_item("video", scrnum, info->vblank_time.attoseconds);
+ }
+
+ /* create spriteram buffers if necessary */
+ if (machine->drv->video_attributes & VIDEO_BUFFERS_SPRITERAM)
+ init_buffered_spriteram();
+
+ /* convert the gfx ROMs into character sets. This is done BEFORE calling the driver's */
+ /* palette_init() routine because it might need to check the machine->gfx[] data */
+ if (machine->drv->gfxdecodeinfo != NULL)
+ allocate_graphics(machine, machine->drv->gfxdecodeinfo);
+
+ /* configure the palette */
+ palette_config(machine);
+
+ /* actually decode the graphics */
+ if (machine->drv->gfxdecodeinfo != NULL)
+ decode_graphics(machine, machine->drv->gfxdecodeinfo);
+
+ /* reset video statics and get out of here */
+ pdrawgfx_shadow_lowpri = 0;
+
+ /* initialize tilemaps */
+ tilemap_init(machine);
+
+ /* create a render target for snapshots */
+ if (viddata->scrinfo[0].state != NULL)
+ {
+ viddata->snap_target = render_target_alloc(layout_snap, RENDER_CREATE_SINGLE_FILE | RENDER_CREATE_HIDDEN);
+ assert(viddata->snap_target != NULL);
+ if (viddata->snap_target == NULL)
+ fatalerror("Unable to allocate snapshot render target\n");
+ render_target_set_layer_config(viddata->snap_target, 0);
+ }
+
+ /* create crosshairs */
+ crosshair_init(viddata);
+
+ /* start recording movie if specified */
+ filename = options_get_string(mame_options(), OPTION_MNGWRITE);
+ if (filename[0] != 0)
+ video_movie_begin_recording(machine, 0, filename);
+}
+
+
+/*-------------------------------------------------
+ video_exit - close down the video system
+-------------------------------------------------*/
+
+static void video_exit(running_machine *machine)
+{
+ video_private *viddata = machine->video_data;
+ int scrnum;
+ int i;
+
+ /* free crosshairs */
+ crosshair_free(viddata);
+
+ /* stop recording any movie */
+ video_movie_end_recording(machine, 0);
+
+ /* free all the graphics elements */
+ for (i = 0; i < MAX_GFX_ELEMENTS; i++)
+ freegfx(machine->gfx[i]);
+
+ /* free all the textures and bitmaps */
+ for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++)
+ {
+ internal_screen_info *info = &viddata->scrinfo[scrnum];
+ if (info->texture[0] != NULL)
+ render_texture_free(info->texture[0]);
+ if (info->texture[1] != NULL)
+ render_texture_free(info->texture[1]);
+ if (info->bitmap[0] != NULL)
+ bitmap_free(info->bitmap[0]);
+ if (info->bitmap[1] != NULL)
+ bitmap_free(info->bitmap[1]);
+ }
+
+ /* free the snapshot target */
+ if (viddata->snap_target != NULL)
+ render_target_free(viddata->snap_target);
+ if (viddata->snap_bitmap != NULL)
+ bitmap_free(viddata->snap_bitmap);
+
+ /* print a final result if we have at least 5 seconds' worth of data */
+ if (global.overall_emutime.seconds >= 5)
+ {
+ osd_ticks_t tps = osd_ticks_per_second();
+ double final_real_time = (double)global.overall_real_seconds + (double)global.overall_real_ticks / (double)tps;
+ double final_emu_time = attotime_to_double(global.overall_emutime);
+ mame_printf_info("Average speed: %.2f%% (%d seconds)\n", 100 * final_emu_time / final_real_time, attotime_add_attoseconds(global.overall_emutime, ATTOSECONDS_PER_SECOND / 2).seconds);
+ }
+}
+
+
+/*-------------------------------------------------
+ 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
+-------------------------------------------------*/
+
+static void init_buffered_spriteram(void)
+{
+ assert_always(spriteram_size != 0, "Video buffers spriteram but spriteram_size is 0");
+
+ /* allocate memory for the back buffer */
+ buffered_spriteram = auto_malloc(spriteram_size);
+
+ /* register for saving it */
+ state_save_register_global_pointer(buffered_spriteram, spriteram_size);
+
+ /* do the same for the secon back buffer, if present */
+ if (spriteram_2_size)
+ {
+ /* allocate memory */
+ buffered_spriteram_2 = auto_malloc(spriteram_2_size);
+
+ /* register for saving it */
+ state_save_register_global_pointer(buffered_spriteram_2, spriteram_2_size);
+ }
+
+ /* make 16-bit and 32-bit pointer variants */
+ buffered_spriteram16 = (UINT16 *)buffered_spriteram;
+ buffered_spriteram32 = (UINT32 *)buffered_spriteram;
+ buffered_spriteram16_2 = (UINT16 *)buffered_spriteram_2;
+ buffered_spriteram32_2 = (UINT32 *)buffered_spriteram_2;
+}
+
+
+
+/***************************************************************************
+ GRAPHICS DECODING
+***************************************************************************/
+
+/*-------------------------------------------------
+ allocate_graphics - allocate memory for the
+ graphics
+-------------------------------------------------*/
+
+static void allocate_graphics(running_machine *machine, const gfx_decode_entry *gfxdecodeinfo)
+{
+ int i;
+
+ /* loop over all elements */
+ for (i = 0; i < MAX_GFX_ELEMENTS && gfxdecodeinfo[i].memory_region != -1; i++)
+ {
+ int region_length = 8 * memory_region_length(gfxdecodeinfo[i].memory_region);
+ int xscale = (gfxdecodeinfo[i].xscale == 0) ? 1 : gfxdecodeinfo[i].xscale;
+ int yscale = (gfxdecodeinfo[i].yscale == 0) ? 1 : gfxdecodeinfo[i].yscale;
+ UINT32 extxoffs[MAX_ABS_GFX_SIZE], extyoffs[MAX_ABS_GFX_SIZE];
+ gfx_layout glcopy;
+ int j;
+
+ /* make a copy of the layout */
+ glcopy = *gfxdecodeinfo[i].gfxlayout;
+
+ /* copy the X and Y offsets into temporary arrays */
+ memcpy(extxoffs, glcopy.xoffset, sizeof(glcopy.xoffset));
+ memcpy(extyoffs, glcopy.yoffset, sizeof(glcopy.yoffset));
+
+ /* if there are extended offsets, copy them over top */
+ if (glcopy.extxoffs != NULL)
+ memcpy(extxoffs, glcopy.extxoffs, glcopy.width * sizeof(extxoffs[0]));
+ if (glcopy.extyoffs != NULL)
+ memcpy(extyoffs, glcopy.extyoffs, glcopy.height * sizeof(extyoffs[0]));
+
+ /* always use the extended offsets here */
+ glcopy.extxoffs = extxoffs;
+ glcopy.extyoffs = extyoffs;
+
+ /* expand X and Y by the scale factors */
+ if (xscale > 1)
+ {
+ glcopy.width *= xscale;
+ for (j = glcopy.width - 1; j >= 0; j--)
+ extxoffs[j] = extxoffs[j / xscale];
+ }
+ if (yscale > 1)
+ {
+ glcopy.height *= yscale;
+ for (j = glcopy.height - 1; j >= 0; j--)
+ extyoffs[j] = extyoffs[j / yscale];
+ }
+
+ /* if the character count is a region fraction, compute the effective total */
+ if (IS_FRAC(glcopy.total))
+ {
+ if (region_length == 0)
+ continue;
+ glcopy.total = region_length / glcopy.charincrement * FRAC_NUM(glcopy.total) / FRAC_DEN(glcopy.total);
+ }
+
+ /* for non-raw graphics, decode the X and Y offsets */
+ if (glcopy.planeoffset[0] != GFX_RAW)
+ {
+ /* loop over all the planes, converting fractions */
+ for (j = 0; j < glcopy.planes; j++)
+ {
+ UINT32 value = glcopy.planeoffset[j];
+ if (IS_FRAC(value))
+ glcopy.planeoffset[j] = FRAC_OFFSET(value) + region_length * FRAC_NUM(value) / FRAC_DEN(value);
+ }
+
+ /* loop over all the X/Y offsets, converting fractions */
+ for (j = 0; j < glcopy.width; j++)
+ {
+ UINT32 value = extxoffs[j];
+ if (IS_FRAC(value))
+ extxoffs[j] = FRAC_OFFSET(value) + region_length * FRAC_NUM(value) / FRAC_DEN(value);
+ }
+
+ for (j = 0; j < glcopy.height; j++)
+ {
+ UINT32 value = extyoffs[j];
+ if (IS_FRAC(value))
+ extyoffs[j] = FRAC_OFFSET(value) + region_length * FRAC_NUM(value) / FRAC_DEN(value);
+ }
+ }
+
+ /* otherwise, just use yoffset[0] as the line modulo */
+ else
+ {
+ int base = gfxdecodeinfo[i].start;
+ int end = region_length/8;
+ while (glcopy.total > 0)
+ {
+ int elementbase = base + (glcopy.total - 1) * glcopy.charincrement / 8;
+ int lastpixelbase = elementbase + glcopy.height * glcopy.yoffset[0] / 8 - 1;
+ if (lastpixelbase < end)
+ break;
+ glcopy.total--;
+ }
+ }
+
+ /* allocate the graphics */
+ machine->gfx[i] = allocgfx(&glcopy);
+
+ /* if we have a remapped colortable, point our local colortable to it */
+ machine->gfx[i]->total_colors = gfxdecodeinfo[i].total_color_codes;
+ machine->gfx[i]->color_base = machine->drv->gfxdecodeinfo[i].color_codes_start;
+ }
+}
+
+
+/*-------------------------------------------------
+ decode_graphics - decode the graphics
+-------------------------------------------------*/
+
+static void decode_graphics(running_machine *machine, const gfx_decode_entry *gfxdecodeinfo)
+{
+ int totalgfx = 0, curgfx = 0;
+ char buffer[200];
+ int i;
+
+ /* count total graphics elements */
+ for (i = 0; i < MAX_GFX_ELEMENTS; i++)
+ if (machine->gfx[i])
+ totalgfx += machine->gfx[i]->total_elements;
+
+ /* loop over all elements */
+ for (i = 0; i < MAX_GFX_ELEMENTS; i++)
+ if (machine->gfx[i] != NULL)
+ {
+ /* if we have a valid region, decode it now */
+ if (gfxdecodeinfo[i].memory_region > REGION_INVALID)
+ {
+ UINT8 *region_base = memory_region(gfxdecodeinfo[i].memory_region);
+ gfx_element *gfx = machine->gfx[i];
+ int j;
+
+ /* now decode the actual graphics */
+ for (j = 0; j < gfx->total_elements; j += 1024)
+ {
+ int num_to_decode = (j + 1024 < gfx->total_elements) ? 1024 : (gfx->total_elements - j);
+ decodegfx(gfx, region_base + gfxdecodeinfo[i].start, j, num_to_decode);
+ curgfx += num_to_decode;
+
+ /* display some startup text */
+ sprintf(buffer, "Decoding (%d%%)", curgfx * 100 / totalgfx);
+ ui_set_startup_text(buffer, FALSE);
+ }
+ }
+
+ /* otherwise, clear the target region */
+ else
+ memset(machine->gfx[i]->gfxdata, 0, machine->gfx[i]->char_modulo * machine->gfx[i]->total_elements);
+ }
+}
+
+
+
+/***************************************************************************
+ SCREEN MANAGEMENT
+***************************************************************************/
+
+/*-------------------------------------------------
+ video_screen_configure - configure the parameters
+ of a screen
+-------------------------------------------------*/
+
+void video_screen_configure(int scrnum, int width, int height, const rectangle *visarea, attoseconds_t refresh)
+{
+ video_private *viddata = Machine->video_data;
+ internal_screen_info *info = &viddata->scrinfo[scrnum];
+
+ /* reallocate bitmap if necessary */
+ if (!(Machine->drv->video_attributes & VIDEO_TYPE_VECTOR))
+ {
+ int curwidth = 0, curheight = 0;
+
+ /* reality checks */
+ if (visarea->min_x < 0 || visarea->min_y < 0 || visarea->max_x >= width || visarea->max_y >= height)
+ fatalerror("video_screen_configure(): visible area must be contained within the width/height!");
+
+ /* extract the current width/height from the bitmap */
+ if (info->bitmap[0] != NULL)
+ {
+ curwidth = info->bitmap[0]->width;
+ curheight = info->bitmap[0]->height;
+ }
+
+ /* if we're too small to contain this width/height, reallocate our bitmaps and textures */
+ if (width > curwidth || height > curheight)
+ {
+ bitmap_format screen_format = info->state->format;
+
+ /* free what we have currently */
+ if (info->texture[0] != NULL)
+ render_texture_free(info->texture[0]);
+ if (info->texture[1] != NULL)
+ render_texture_free(info->texture[1]);
+ if (info->bitmap[0] != NULL)
+ bitmap_free(info->bitmap[0]);
+ if (info->bitmap[1] != NULL)
+ bitmap_free(info->bitmap[1]);
+
+ /* compute new width/height */
+ curwidth = MAX(width, curwidth);
+ curheight = MAX(height, curheight);
+
+ /* choose the texture format */
+ /* convert the screen format to a texture format */
+ switch (screen_format)
+ {
+ case BITMAP_FORMAT_INDEXED16: info->format = TEXFORMAT_PALETTE16; break;
+ case BITMAP_FORMAT_RGB15: info->format = TEXFORMAT_RGB15; break;
+ case BITMAP_FORMAT_RGB32: info->format = TEXFORMAT_RGB32; break;
+ default: fatalerror("Invalid bitmap format!"); break;
+ }
+
+ /* allocate bitmaps */
+ info->bitmap[0] = bitmap_alloc(curwidth, curheight, screen_format);
+ bitmap_set_palette(info->bitmap[0], Machine->palette);
+ info->bitmap[1] = bitmap_alloc(curwidth, curheight, screen_format);
+ bitmap_set_palette(info->bitmap[1], Machine->palette);
+
+ /* allocate textures */
+ info->texture[0] = render_texture_alloc(NULL, NULL);
+ render_texture_set_bitmap(info->texture[0], info->bitmap[0], visarea, info->config->palette_base, info->format);
+ info->texture[1] = render_texture_alloc(NULL, NULL);
+ render_texture_set_bitmap(info->texture[1], info->bitmap[1], visarea, info->config->palette_base, info->format);
+ }
+ }
+
+ /* now fill in the new parameters */
+ info->state->width = width;
+ info->state->height = height;
+ info->state->visarea = *visarea;
+ info->state->refresh = refresh;
+
+ /* compute timing parameters */
+ info->scantime = refresh / height;
+ info->pixeltime = refresh / (height * width);
+
+ /* adjust speed if necessary */
+ if (global.refresh_speed)
+ {
+ float minrefresh = render_get_max_update_rate();
+ if (minrefresh != 0)
+ {
+ UINT32 target_speed = floor(minrefresh * 100.0 / ATTOSECONDS_TO_HZ(refresh));
+ target_speed = MIN(target_speed, global.original_speed);
+ if (target_speed != global.speed)
+ {
+ mame_printf_verbose("Adjusting target speed to %d%%\n", target_speed);
+ global.speed = target_speed;
+ }
+ }
+ }
+
+ /* recompute the VBLANK timing */
+ cpu_compute_vblank_timing();
+
+ /* 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(info->scanline0_timer, attotime_zero, scrnum, attotime_zero);
+ else
+ timer_adjust(info->scanline0_timer, video_screen_get_time_until_pos(scrnum, 0, 0), scrnum, attotime_zero);
+}
+
+
+/*-------------------------------------------------
+ video_screen_set_visarea - just set the visible area
+ of a screen
+-------------------------------------------------*/
+
+void video_screen_set_visarea(int scrnum, int min_x, int max_x, int min_y, int max_y)
+{
+ screen_state *state = &Machine->screen[scrnum];
+ rectangle visarea;
+
+ visarea.min_x = min_x;
+ visarea.max_x = max_x;
+ visarea.min_y = min_y;
+ visarea.max_y = max_y;
+
+ video_screen_configure(scrnum, state->width, state->height, &visarea, state->refresh);
+}
+
+
+/*-------------------------------------------------
+ video_screen_exists - returns whether a given
+ screen exists
+-------------------------------------------------*/
+
+int video_screen_exists(int scrnum)
+{
+ return (scrnum >= 0 && scrnum < MAX_SCREENS && Machine->drv->screen[scrnum].tag != NULL);
+}
+
+
+/*-------------------------------------------------
+ get_screen_info - accessor function to get
+ private data for a screen
+-------------------------------------------------*/
+
+static internal_screen_info *get_screen_info(running_machine *machine, int scrnum)
+{
+ video_private *viddata = machine->video_data;
+ assert_always(video_screen_exists(scrnum), "Invalid screen");
+ return &viddata->scrinfo[scrnum];
+}
+
+
+/*-------------------------------------------------
+ video_screen_update_partial - perform a partial
+ update from the last scanline up to and
+ including the specified scanline
+-------------------------------------------------*/
+
+void video_screen_update_partial(int scrnum, int scanline)
+{
+ internal_screen_info *info = get_screen_info(Machine, scrnum);
+ rectangle clip = info->state->visarea;
+
+ LOG_PARTIAL_UPDATES(("Partial: video_screen_update_partial(%d,%d): ", scrnum, scanline));
+
+ /* these two checks only apply if we're allowed to skip frames */
+ if (!(Machine->drv->video_attributes & VIDEO_ALWAYS_UPDATE))
+ {
+ /* if skipping this frame, bail */
+ if (global.skipping_this_frame)
+ {
+ LOG_PARTIAL_UPDATES(("skipped due to frameskipping\n"));
+ return;
+ }
+
+ /* skip if this screen is not visible anywhere */
+ if (!(render_get_live_screens_mask() & (1 << scrnum)))
+ {
+ LOG_PARTIAL_UPDATES(("skipped because screen not live\n"));
+ return;
+ }
+ }
+
+ /* skip if less than the lowest so far */
+ if (scanline < info->last_partial_scan)
+ {
+ LOG_PARTIAL_UPDATES(("skipped because less than previous\n"));
+ return;
+ }
+
+ /* set the start/end scanlines */
+ if (info->last_partial_scan > clip.min_y)
+ clip.min_y = info->last_partial_scan;
+ if (scanline < clip.max_y)
+ clip.max_y = scanline;
+
+ /* render if necessary */
+ if (clip.min_y <= clip.max_y)
+ {
+ UINT32 flags = UPDATE_HAS_NOT_CHANGED;
+
+ profiler_mark(PROFILER_VIDEO);
+ LOG_PARTIAL_UPDATES(("updating %d-%d\n", clip.min_y, clip.max_y));
+
+ if (Machine->drv->video_update != NULL)
+ flags = (*Machine->drv->video_update)(Machine, scrnum, info->bitmap[info->curbitmap], &clip);
+ global.partial_updates_this_frame++;
+ profiler_mark(PROFILER_END);
+
+ /* if we modified the bitmap, we have to commit */
+ info->changed |= ~flags & UPDATE_HAS_NOT_CHANGED;
+ }
+
+ /* remember where we left off */
+ info->last_partial_scan = scanline + 1;
+}
+
+
+/*-------------------------------------------------
+ video_screen_get_vpos - returns the current
+ vertical position of the beam for a given
+ screen
+-------------------------------------------------*/
+
+int video_screen_get_vpos(int scrnum)
+{
+ internal_screen_info *info = get_screen_info(Machine, scrnum);
+ attoseconds_t delta = attotime_to_attoseconds(attotime_sub(timer_get_time(), info->vblank_time));
+ int vpos;
+
+ /* round to the nearest pixel */
+ delta += info->pixeltime / 2;
+
+ /* compute the v position relative to the start of VBLANK */
+ vpos = delta / info->scantime;
+
+ /* adjust for the fact that VBLANK starts at the bottom of the visible area */
+ return (info->state->visarea.max_y + 1 + vpos) % info->state->height;
+}
+
+
+/*-------------------------------------------------
+ video_screen_get_hpos - returns the current
+ horizontal position of the beam for a given
+ screen
+-------------------------------------------------*/
+
+int video_screen_get_hpos(int scrnum)
+{
+ internal_screen_info *info = get_screen_info(Machine, scrnum);
+ attoseconds_t delta = attotime_to_attoseconds(attotime_sub(timer_get_time(), info->vblank_time));
+ int vpos;
+
+ /* round to the nearest pixel */
+ delta += info->pixeltime / 2;
+
+ /* compute the v position relative to the start of VBLANK */
+ vpos = delta / info->scantime;
+
+ /* subtract that from the total time */
+ delta -= vpos * info->scantime;
+
+ /* return the pixel offset from the start of this scanline */
+ return delta / info->pixeltime;
+}
+
+
+/*-------------------------------------------------
+ video_screen_get_vblank - returns the VBLANK
+ state of a given screen
+-------------------------------------------------*/
+
+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);
+}
+
+
+/*-------------------------------------------------
+ video_screen_get_hblank - returns the HBLANK
+ state of a given screen
+-------------------------------------------------*/
+
+int video_screen_get_hblank(int scrnum)
+{
+ internal_screen_info *info = get_screen_info(Machine, scrnum);
+ int hpos = video_screen_get_hpos(scrnum);
+ return (hpos < info->state->visarea.min_x || hpos > info->state->visarea.max_x);
+}
+
+
+/*-------------------------------------------------
+ video_screen_get_time_until_pos - returns the
+ amount of time remaining until the beam is
+ at the given hpos,vpos
+-------------------------------------------------*/
+
+attotime video_screen_get_time_until_pos(int scrnum, int vpos, int hpos)
+{
+ internal_screen_info *info = get_screen_info(Machine, scrnum);
+ attoseconds_t curdelta = attotime_to_attoseconds(attotime_sub(timer_get_time(), info->vblank_time));
+ attoseconds_t targetdelta;
+
+ /* since we measure time relative to VBLANK, compute the scanline offset from VBLANK */
+ vpos += info->state->height - (info->state->visarea.max_y + 1);
+ vpos %= info->state->height;
+
+ /* compute the delta for the given X,Y position */
+ targetdelta = (attoseconds_t)vpos * info->scantime + (attoseconds_t)hpos * info->pixeltime;
+
+ /* if we're past that time (within 1/2 of a pixel), head to the next frame */
+ if (targetdelta <= curdelta + info->pixeltime / 2)
+ targetdelta += info->state->refresh;
+ while (targetdelta <= curdelta)
+ targetdelta += info->state->refresh;
+
+ /* return the difference */
+ return attotime_make(0, targetdelta - curdelta);
+}
+
+
+/*-------------------------------------------------
+ video_screen_get_scan_period - return the
+ amount of time the beam takes to draw one
+ scanline
+-------------------------------------------------*/
+
+attotime video_screen_get_scan_period(int scrnum)
+{
+ internal_screen_info *info = get_screen_info(Machine, scrnum);
+ return attotime_make(0, info->scantime);
+}
+
+
+/*-------------------------------------------------
+ video_screen_get_frame_period - return the
+ amount of time the beam takes to draw one
+ complete frame
+-------------------------------------------------*/
+
+attotime video_screen_get_frame_period(int scrnum)
+{
+ return attotime_make(0, Machine->screen[scrnum].refresh);
+}
+
+
+
+/***************************************************************************
+ GLOBAL RENDERING
+***************************************************************************/
+
+/*-------------------------------------------------
+ scanline0_callback - reset partial updates
+ for a screen
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( scanline0_callback )
+{
+ video_private *viddata = machine->video_data;
+ int scrnum = param;
+
+ /* reset partial updates */
+ viddata->scrinfo[scrnum].last_partial_scan = 0;
+ global.partial_updates_this_frame = 0;
+
+ timer_adjust(viddata->scrinfo[scrnum].scanline0_timer, video_screen_get_time_until_pos(scrnum, 0, 0), scrnum, attotime_zero);
+}
+
+
+/*-------------------------------------------------
+ video_frame_update - handle frameskipping and
+ UI, plus updating the screen during normal
+ operations
+-------------------------------------------------*/
+
+void video_frame_update(void)
+{
+ attotime current_time = timer_get_time();
+ int skipped_it = global.skipping_this_frame;
+ int phase = mame_get_phase(Machine);
+
+ /* only render sound and video if we're in the running phase */
+#ifdef MAME_DEBUG
+ if (phase == MAME_PHASE_RUNNING)
+#else
+ if (phase == MAME_PHASE_RUNNING && !mame_is_paused(Machine))
+#endif
+ {
+ int anything_changed = finish_screen_updates(Machine);
+
+ /* if none of the screens changed and we haven't skipped too many frames in a row,
+ mark this frame as skipped to prevent throttling; this helps for games that
+ don't update their screen at the monitor refresh rate */
+ if (!anything_changed && !global.auto_frameskip && global.frameskip_level == 0 && global.empty_skip_count++ < 3)
+ skipped_it = TRUE;
+ else
+ global.empty_skip_count = 0;
+ }
+
+ /* draw the user interface */
+ ui_update_and_render();
+
+ /* if we're throttling, synchronize before rendering */
+ if (!skipped_it && effective_throttle())
+ update_throttle(current_time);
+
+ /* ask the OSD to update */
+ profiler_mark(PROFILER_BLIT);
+ osd_update(skipped_it);
+ profiler_mark(PROFILER_END);
+
+ /* perform tasks for this frame */
+ mame_frame_update(Machine);
+
+ /* update frameskipping */
+ update_frameskip();
+
+ /* update speed computations */
+ if (!skipped_it)
+ recompute_speed(current_time);
+
+ /* call the end-of-frame callback */
+ if (phase == MAME_PHASE_RUNNING)
+ {
+ /* reset partial updates if we're paused or if the debugger is active */
+ if (video_screen_exists(0) && (mame_is_paused(Machine) || mame_debug_is_active()))
+ scanline0_callback(Machine, 0);
+
+ /* otherwise, call the video EOF callback */
+ else if (Machine->drv->video_eof != NULL)
+ {
+ profiler_mark(PROFILER_VIDEO);
+ (*Machine->drv->video_eof)(Machine);
+ profiler_mark(PROFILER_END);
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ finish_screen_updates - finish updating all
+ the screens
+-------------------------------------------------*/
+
+static int finish_screen_updates(running_machine *machine)
+{
+ video_private *viddata = machine->video_data;
+ int anything_changed = FALSE;
+ int livemask;
+ int scrnum;
+
+ /* finish updating the screens */
+ for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++)
+ if (machine->drv->screen[scrnum].tag != NULL)
+ video_screen_update_partial(scrnum, machine->screen[scrnum].visarea.max_y);
+
+ /* now add the quads for all the screens */
+ livemask = render_get_live_screens_mask();
+ for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++)
+ {
+ internal_screen_info *screen = &viddata->scrinfo[scrnum];
+
+ /* only update if live */
+ if (livemask & (1 << scrnum))
+ {
+ /* only update if empty and not a vector game; otherwise assume the driver did it directly */
+ if ((machine->drv->video_attributes & (VIDEO_TYPE_VECTOR | VIDEO_SELF_RENDER)) == 0)
+ {
+ /* if we're not skipping the frame and if the screen actually changed, then update the texture */
+ if (!global.skipping_this_frame && screen->changed)
+ {
+ mame_bitmap *bitmap = screen->bitmap[screen->curbitmap];
+ rectangle fixedvis = machine->screen[scrnum].visarea;
+ fixedvis.max_x++;
+ fixedvis.max_y++;
+ render_texture_set_bitmap(screen->texture[screen->curbitmap], bitmap, &fixedvis, machine->drv->screen[scrnum].palette_base, screen->format);
+ screen->curtexture = screen->curbitmap;
+ screen->curbitmap = 1 - screen->curbitmap;
+ }
+
+ /* create an empty container with a single quad */
+ render_container_empty(render_container_get_screen(scrnum));
+ render_screen_add_quad(scrnum, 0.0f, 0.0f, 1.0f, 1.0f, MAKE_ARGB(0xff,0xff,0xff,0xff), screen->texture[screen->curtexture], PRIMFLAG_BLENDMODE(BLENDMODE_NONE) | PRIMFLAG_SCREENTEX(1));
+ }
+
+ /* update our movie recording state */
+ if (!mame_is_paused(machine))
+ movie_record_frame(machine, scrnum);
+ }
+
+ /* reset the screen changed flags */
+ if (screen->changed)
+ anything_changed = TRUE;
+ screen->changed = FALSE;
+ }
+
+ /* draw any crosshairs */
+ crosshair_render(viddata);
+ return anything_changed;
+}
+
+
+
+/***************************************************************************
+ THROTTLING/FRAMESKIPPING/PERFORMANCE
+***************************************************************************/
+
+/*-------------------------------------------------
+ video_skip_this_frame - accessor to determine
+ if this frame is being skipped
+-------------------------------------------------*/
+
+int video_skip_this_frame(void)
+{
+ return global.skipping_this_frame;
+}
+
+
+/*-------------------------------------------------
+ video_get_speed_factor - return the speed
+ factor as an integer * 100
+-------------------------------------------------*/
+
+int video_get_speed_factor(void)
+{
+ return global.speed;
+}
+
+
+/*-------------------------------------------------
+ video_set_speed_factor - sets the speed
+ factor as an integer * 100
+-------------------------------------------------*/
+
+void video_set_speed_factor(int speed)
+{
+ global.speed = speed;
+}
+
+
+/*-------------------------------------------------
+ video_get_speed_text - print the text to
+ be displayed in the upper-right corner
+-------------------------------------------------*/
+
+const char *video_get_speed_text(void)
+{
+ int paused = mame_is_paused(Machine);
+ static char buffer[1024];
+ char *dest = buffer;
+
+ /* if we're paused, just display Paused */
+ if (paused)
+ dest += sprintf(dest, "paused");
+
+ /* if we're fast forwarding, just display Fast-forward */
+ else if (global.fastforward)
+ dest += sprintf(dest, "fast ");
+
+ /* if we're auto frameskipping, display that plus the level */
+ else if (effective_autoframeskip())
+ dest += sprintf(dest, "auto%2d/%d", effective_frameskip(), MAX_FRAMESKIP);
+
+ /* otherwise, just display the frameskip plus the level */
+ else
+ dest += sprintf(dest, "skip %d/%d", effective_frameskip(), MAX_FRAMESKIP);
+
+ /* append the speed for all cases except paused */
+ if (!paused)
+ dest += sprintf(dest, "%4d%%", (int)(100 * global.speed_percent + 0.5));
+
+ /* display the number of partial updates as well */
+ if (global.partial_updates_this_frame > 1)
+ dest += sprintf(dest, "\n%d partial updates", global.partial_updates_this_frame);
+
+ /* return a pointer to the static buffer */
+ return buffer;
+}
+
+
+/*-------------------------------------------------
+ video_get_frameskip - return the current
+ actual frameskip (-1 means autoframeskip)
+-------------------------------------------------*/
+
+int video_get_frameskip(void)
+{
+ /* if autoframeskip is on, return -1 */
+ if (global.auto_frameskip)
+ return -1;
+
+ /* otherwise, return the direct level */
+ else
+ return global.frameskip_level;
+}
+
+
+/*-------------------------------------------------
+ video_set_frameskip - set the current
+ actual frameskip (-1 means autoframeskip)
+-------------------------------------------------*/
+
+void video_set_frameskip(int frameskip)
+{
+ /* -1 means autoframeskip */
+ if (frameskip == -1)
+ {
+ global.auto_frameskip = TRUE;
+ global.frameskip_level = 0;
+ }
+
+ /* any other level is a direct control */
+ else if (frameskip >= 0 && frameskip <= MAX_FRAMESKIP)
+ {
+ global.auto_frameskip = FALSE;
+ global.frameskip_level = frameskip;
+ }
+}
+
+
+/*-------------------------------------------------
+ video_get_throttle - return the current
+ actual throttle
+-------------------------------------------------*/
+
+int video_get_throttle(void)
+{
+ return global.throttle;
+}
+
+
+/*-------------------------------------------------
+ video_set_throttle - set the current
+ actual throttle
+-------------------------------------------------*/
+
+void video_set_throttle(int throttle)
+{
+ global.throttle = throttle;
+}
+
+
+/*-------------------------------------------------
+ video_get_fastforward - return the current
+ fastforward value
+-------------------------------------------------*/
+
+int video_get_fastforward(void)
+{
+ return global.fastforward;
+}
+
+
+/*-------------------------------------------------
+ video_set_fastforward - set the current
+ fastforward value
+-------------------------------------------------*/
+
+void video_set_fastforward(int _fastforward)
+{
+ global.fastforward = _fastforward;
+}
+
+
+/*-------------------------------------------------
+ update_throttle - throttle to the game's
+ natural speed
+-------------------------------------------------*/
+
+static void update_throttle(attotime emutime)
+{
+/*
+
+ Throttling theory:
+
+ This routine is called periodically with an up-to-date emulated time.
+ The idea is to synchronize real time with emulated time. We do this
+ by "throttling", or waiting for real time to catch up with emulated
+ time.
+
+ In an ideal world, it will take less real time to emulate and render
+ each frame than the emulated time, so we need to slow things down to
+ get both times in sync.
+
+ There are many complications to this model:
+
+ * some games run too slow, so each frame we get further and
+ further behind real time; our only choice here is to not
+ throttle
+
+ * some games have very uneven frame rates; one frame will take
+ a long time to emulate, and the next frame may be very fast
+
+ * we run on top of multitasking OSes; sometimes execution time
+ is taken away from us, and this means we may not get enough
+ time to emulate one frame
+
+ * we may be paused, and emulated time may not be marching
+ forward
+
+ * emulated time could jump due to resetting the machine or
+ restoring from a saved state
+
+*/
+ static const UINT8 popcount[256] =
+ {
+ 0,1,1,2,1,2,2,3, 1,2,2,3,2,3,3,4, 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5,
+ 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6,
+ 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6,
+ 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7,
+ 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6,
+ 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7,
+ 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7,
+ 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 4,5,5,6,5,6,6,7, 5,6,6,7,6,7,7,8
+ };
+ attoseconds_t real_delta_attoseconds;
+ attoseconds_t emu_delta_attoseconds;
+ attoseconds_t real_is_ahead_attoseconds;
+ attoseconds_t attoseconds_per_tick;
+ osd_ticks_t ticks_per_second;
+ osd_ticks_t target_ticks;
+ osd_ticks_t diff_ticks;
+
+ /* apply speed factor to emu time */
+ if (global.speed != 0 && global.speed != 100)
+ {
+ /* multiply emutime by 100 */
+ emutime = attotime_mul(emutime, 100);
+
+ /* divide emutime by the global speed factor */
+ emutime.attoseconds /= global.speed;
+ emutime.attoseconds += (emutime.seconds % global.speed) * (ATTOSECONDS_PER_SECOND / global.speed);
+ emutime.seconds /= global.speed;
+ }
+
+ /* compute conversion factors up front */
+ ticks_per_second = osd_ticks_per_second();
+ attoseconds_per_tick = ATTOSECONDS_PER_SECOND / ticks_per_second;
+
+ /* if we're paused, emutime will not advance; instead, we subtract a fixed
+ amount of time (1/60th of a second) from the emulated time that was passed in,
+ and explicitly reset our tracked real and emulated timers to that value ...
+ this means we pretend that the last update was exactly 1/60th of a second
+ ago, and was in sync in both real and emulated time */
+ if (mame_is_paused(Machine))
+ {
+ global.throttle_emutime = attotime_sub_attoseconds(emutime, ATTOSECONDS_PER_SECOND / PAUSED_REFRESH_RATE);
+ global.throttle_realtime = global.throttle_emutime;
+ }
+
+ /* attempt to detect anomalies in the emulated time by subtracting the previously
+ reported value from our current value; this should be a small value somewhere
+ between 0 and 1/10th of a second ... anything outside of this range is obviously
+ wrong and requires a resync */
+ emu_delta_attoseconds = attotime_to_attoseconds(attotime_sub(emutime, global.throttle_emutime));
+ if (emu_delta_attoseconds < 0 || emu_delta_attoseconds > ATTOSECONDS_PER_SECOND / 10)
+ {
+ if (LOG_THROTTLE)
+ logerror("Resync due to weird emutime delta: %s\n", attotime_string(attotime_make(0, emu_delta_attoseconds), 18));
+ goto resync;
+ }
+
+ /* now determine the current real time in OSD-specified ticks; we have to be careful
+ here because counters can wrap, so we only use the difference between the last
+ read value and the current value in our computations */
+ diff_ticks = osd_ticks() - global.throttle_last_ticks;
+ global.throttle_last_ticks += diff_ticks;
+
+ /* if it has been more than a full second of real time since the last call to this
+ function, we just need to resynchronize */
+ if (diff_ticks >= ticks_per_second)
+ {
+ if (LOG_THROTTLE)
+ logerror("Resync due to real time advancing by more than 1 second\n");
+ goto resync;
+ }
+
+ /* convert this value into attoseconds for easier comparison */
+ real_delta_attoseconds = diff_ticks * attoseconds_per_tick;
+
+ /* now update our real and emulated timers with the current values */
+ global.throttle_emutime = emutime;
+ global.throttle_realtime = attotime_add_attoseconds(global.throttle_realtime, real_delta_attoseconds);
+
+ /* keep a history of whether or not emulated time beat real time over the last few
+ updates; this can be used for future heuristics */
+ global.throttle_history = (global.throttle_history << 1) | (emu_delta_attoseconds > real_delta_attoseconds);
+
+ /* determine how far ahead real time is versus emulated time; note that we use the
+ accumulated times for this instead of the deltas for the current update because
+ we want to track time over a longer duration than a single update */
+ real_is_ahead_attoseconds = attotime_to_attoseconds(attotime_sub(global.throttle_emutime, global.throttle_realtime));
+
+ /* if we're more than 1/10th of a second out, or if we are behind at all and emulation
+ is taking longer than the real frame, we just need to resync */
+ if (real_is_ahead_attoseconds < -ATTOSECONDS_PER_SECOND / 10 ||
+ (real_is_ahead_attoseconds < 0 && popcount[global.throttle_history & 0xff] < 6))
+ {
+ if (LOG_THROTTLE)
+ logerror("Resync due to being behind: %s (history=%08X)\n", attotime_string(attotime_make(0, -real_is_ahead_attoseconds), 18), global.throttle_history);
+ goto resync;
+ }
+
+ /* if we're behind, it's time to just get out */
+ if (real_is_ahead_attoseconds < 0)
+ return;
+
+ /* compute the target real time, in ticks, where we want to be */
+ target_ticks = global.throttle_last_ticks + real_is_ahead_attoseconds / attoseconds_per_tick;
+
+ /* throttle until we read the target, and update real time to match the final time */
+ diff_ticks = throttle_until_ticks(target_ticks) - global.throttle_last_ticks;
+ global.throttle_last_ticks += diff_ticks;
+ global.throttle_realtime = attotime_add_attoseconds(global.throttle_realtime, diff_ticks * attoseconds_per_tick);
+ return;
+
+resync:
+ /* reset realtime and emutime to the same value */
+ global.throttle_realtime = global.throttle_emutime = emutime;
+}
+
+
+/*-------------------------------------------------
+ throttle_until_ticks - spin until the
+ specified target time, calling the OSD code
+ to sleep if possible
+-------------------------------------------------*/
+
+static osd_ticks_t throttle_until_ticks(osd_ticks_t target_ticks)
+{
+ osd_ticks_t minimum_sleep = osd_ticks_per_second() / 1000;
+ osd_ticks_t current_ticks = osd_ticks();
+ osd_ticks_t new_ticks;
+ int allowed_to_sleep;
+
+ /* we're allowed to sleep via the OSD code only if we're configured to do so
+ and we're not frameskipping due to autoframeskip, or if we're paused */
+ allowed_to_sleep = mame_is_paused(Machine) ||
+ (global.sleep && (!effective_autoframeskip() || effective_frameskip() == 0));
+
+ /* loop until we reach our target */
+ profiler_mark(PROFILER_IDLE);
+ while (current_ticks < target_ticks)
+ {
+ osd_ticks_t delta;
+ int slept = FALSE;
+
+ /* compute how much time to sleep for, taking into account the average oversleep */
+ delta = (target_ticks - current_ticks) * 1000 / (1000 + global.average_oversleep);
+
+ /* see if we can sleep */
+ if (allowed_to_sleep && delta >= minimum_sleep)
+ {
+ osd_sleep(delta);
+ slept = TRUE;
+ }
+
+ /* read the new value */
+ new_ticks = osd_ticks();
+
+ /* keep some metrics on the sleeping patterns of the OSD layer */
+ if (slept)
+ {
+ osd_ticks_t actual_ticks = new_ticks - current_ticks;
+
+ /* if we overslept, keep an average of the amount */
+ if (actual_ticks > delta)
+ {
+ osd_ticks_t oversleep_milliticks = 1000 * (actual_ticks - delta) / delta;
+
+ /* take 90% of the previous average plus 10% of the new value */
+ global.average_oversleep = (global.average_oversleep * 99 + oversleep_milliticks) / 100;
+
+ if (LOG_THROTTLE)
+ logerror("Slept for %d ticks, got %d ticks, avgover = %d\n", (int)delta, (int)actual_ticks, (int)global.average_oversleep);
+ }
+ }
+ current_ticks = new_ticks;
+ }
+
+ return current_ticks;
+}
+
+
+/*-------------------------------------------------
+ update_frameskip - update frameskipping
+ counters and periodically update autoframeskip
+-------------------------------------------------*/
+
+static void update_frameskip(void)
+{
+ /* if we're throttling and autoframeskip is on, adjust */
+ if (effective_throttle() && effective_autoframeskip() && global.frameskip_counter == 0)
+ {
+ float speed = global.speed * 0.01;
+
+ /* if we're too fast, attempt to increase the frameskip */
+ if (global.speed_percent >= 0.995 * speed)
+ {
+ /* but only after 3 consecutive frames where we are too fast */
+ if (++global.frameskip_adjust >= 3)
+ {
+ global.frameskip_adjust = 0;
+ if (global.frameskip_level > 0)
+ global.frameskip_level--;
+ }
+ }
+
+ /* if we're too slow, attempt to increase the frameskip */
+ else
+ {
+ /* if below 80% speed, be more aggressive */
+ if (global.speed_percent < 0.80 * speed)
+ global.frameskip_adjust -= (0.90 * speed - global.speed_percent) / 0.05;
+
+ /* if we're close, only force it up to frameskip 8 */
+ else if (global.frameskip_level < 8)
+ global.frameskip_adjust--;
+
+ /* perform the adjustment */
+ while (global.frameskip_adjust <= -2)
+ {
+ global.frameskip_adjust += 2;
+ if (global.frameskip_level < MAX_FRAMESKIP)
+ global.frameskip_level++;
+ }
+ }
+ }
+
+ /* increment the frameskip counter and determine if we will skip the next frame */
+ global.frameskip_counter = (global.frameskip_counter + 1) % FRAMESKIP_LEVELS;
+ global.skipping_this_frame = skiptable[effective_frameskip()][global.frameskip_counter];
+}
+
+
+/*-------------------------------------------------
+ recompute_speed - recompute the current
+ overall speed; we assume this is called only
+ if we did not skip a frame
+-------------------------------------------------*/
+
+static void recompute_speed(attotime emutime)
+{
+ attoseconds_t delta_emutime;
+
+ /* if we don't have a starting time yet, or if we're paused, reset our starting point */
+ if (global.speed_last_realtime == 0 || mame_is_paused(Machine))
+ {
+ global.speed_last_realtime = osd_ticks();
+ global.speed_last_emutime = emutime;
+ }
+
+ /* if it has been more than the update interval, update the time */
+ delta_emutime = attotime_to_attoseconds(attotime_sub(emutime, global.speed_last_emutime));
+ if (delta_emutime > SUBSECONDS_PER_SPEED_UPDATE)
+ {
+ osd_ticks_t realtime = osd_ticks();
+ osd_ticks_t delta_realtime = realtime - global.speed_last_realtime;
+ osd_ticks_t tps = osd_ticks_per_second();
+
+ /* convert from ticks to attoseconds */
+ global.speed_percent = (double)delta_emutime * (double)tps / ((double)delta_realtime * (double)ATTOSECONDS_PER_SECOND);
+
+ /* remember the last times */
+ global.speed_last_realtime = realtime;
+ global.speed_last_emutime = emutime;
+
+ /* if we're throttled, this time period counts for overall speed; otherwise, we reset the counter */
+ if (!global.fastforward)
+ global.overall_valid_counter++;
+ else
+ global.overall_valid_counter = 0;
+
+ /* if we've had at least 4 consecutive valid periods, accumulate stats */
+ if (global.overall_valid_counter >= 4)
+ {
+ global.overall_real_ticks += delta_realtime;
+ while (global.overall_real_ticks >= tps)
+ {
+ global.overall_real_ticks -= tps;
+ global.overall_real_seconds++;
+ }
+ global.overall_emutime = attotime_add_attoseconds(global.overall_emutime, delta_emutime);
+ }
+ }
+
+ /* if we're past the "time-to-execute" requested, signal an exit */
+ if (global.seconds_to_run != 0 && emutime.seconds >= global.seconds_to_run)
+ {
+ astring *fname = astring_assemble_2(astring_alloc(), Machine->basename, PATH_SEPARATOR "final.png");
+ file_error filerr;
+ mame_file *file;
+
+ /* create a final screenshot */
+ filerr = mame_fopen(SEARCHPATH_SCREENSHOT, astring_c(fname), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &file);
+ if (filerr == FILERR_NONE)
+ {
+ video_screen_save_snapshot(Machine, file, 0);
+ mame_fclose(file);
+ }
+ astring_free(fname);
+
+ /* schedule our demise */
+ mame_schedule_exit(Machine);
+ }
+}
+
+
+
+/***************************************************************************
+ SCREEN SNAPSHOTS
+***************************************************************************/
+
+/*-------------------------------------------------
+ video_screen_save_snapshot - save a snapshot
+ to the given file handle
+-------------------------------------------------*/
+
+void video_screen_save_snapshot(running_machine *machine, mame_file *fp, int scrnum)
+{
+ const rgb_t *palette = (machine->palette != NULL) ? palette_entry_list_adjusted(machine->palette) : NULL;
+ png_info pnginfo = { 0 };
+ mame_bitmap *bitmap;
+ png_error error;
+ char text[256];
+
+ /* generate the bitmap to pass in */
+ bitmap = get_snapshot_bitmap(machine, scrnum);
+ if (bitmap == NULL)
+ return;
+
+ /* add two text entries describing the image */
+ sprintf(text, APPNAME " %s", build_version);
+ png_add_text(&pnginfo, "Software", text);
+ sprintf(text, "%s %s", machine->gamedrv->manufacturer, machine->gamedrv->description);
+ png_add_text(&pnginfo, "System", text);
+
+ /* now do the actual work */
+ error = png_write_bitmap(mame_core_file(fp), &pnginfo, machine->video_data->snap_bitmap, machine->drv->total_colors, palette);
+
+ /* free any data allocated */
+ png_free(&pnginfo);
+}
+
+
+/*-------------------------------------------------
+ video_save_active_screen_snapshots - save a
+ snapshot of all active screens
+-------------------------------------------------*/
+
+void video_save_active_screen_snapshots(running_machine *machine)
+{
+ UINT32 screenmask = render_get_live_screens_mask();
+ mame_file *fp;
+ int scrnum;
+
+ /* write one snapshot per visible screen */
+ for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++)
+ if (screenmask & (1 << scrnum))
+ {
+ file_error filerr = mame_fopen_next(SEARCHPATH_SCREENSHOT, "png", &fp);
+ if (filerr == FILERR_NONE)
+ {
+ video_screen_save_snapshot(machine, fp, scrnum);
+ mame_fclose(fp);
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ get_snapshot_bitmap - return a pointer to the
+ bitmap containing the screenshot for the
+ given screen number
+-------------------------------------------------*/
+
+static mame_bitmap *get_snapshot_bitmap(running_machine *machine, int scrnum)
+{
+ video_private *viddata = machine->video_data;
+ const render_primitive_list *primlist;
+ INT32 width, height;
+
+ assert(scrnum >= 0 && scrnum < MAX_SCREENS);
+
+ /* if no screens, do nothing */
+ if (viddata->snap_target == NULL)
+ return NULL;
+
+ /* select the appropriate view in our dummy target */
+ render_target_set_view(viddata->snap_target, scrnum);
+
+ /* get the minimum width/height and set it on the target */
+ render_target_get_minimum_size(viddata->snap_target, &width, &height);
+ render_target_set_bounds(viddata->snap_target, width, height, 0);
+
+ /* if we don't have a bitmap, or if it's not the right size, allocate a new one */
+ if (viddata->snap_bitmap == NULL || width != viddata->snap_bitmap->width || height != viddata->snap_bitmap->height)
+ {
+ if (viddata->snap_bitmap != NULL)
+ bitmap_free(viddata->snap_bitmap);
+ viddata->snap_bitmap = bitmap_alloc(width, height, BITMAP_FORMAT_RGB32);
+ assert(viddata->snap_bitmap != NULL);
+ }
+
+ /* render the screen there */
+ primlist = render_target_get_primitives(viddata->snap_target);
+ osd_lock_acquire(primlist->lock);
+ rgb888_draw_primitives(primlist->head, viddata->snap_bitmap->base, width, height, viddata->snap_bitmap->rowpixels);
+ osd_lock_release(primlist->lock);
+
+ /* now do the actual work */
+ return viddata->snap_bitmap;
+}
+
+
+/*-------------------------------------------------
+ mame_fopen_next - open the next non-existing
+ file of type filetype according to our
+ numbering scheme
+-------------------------------------------------*/
+
+static file_error mame_fopen_next(const char *pathoption, const char *extension, mame_file **file)
+{
+ file_error filerr;
+ char *fname;
+ int seq;
+
+ /* allocate temp space for the name */
+ fname = malloc_or_die(strlen(Machine->basename) + 1 + 10 + strlen(extension) + 1);
+
+ /* try until we succeed */
+ for (seq = 0; ; seq++)
+ {
+ sprintf(fname, "%s" PATH_SEPARATOR "%04d.%s", Machine->basename, seq, extension);
+ filerr = mame_fopen(pathoption, fname, OPEN_FLAG_READ, file);
+ if (filerr != FILERR_NONE)
+ break;
+ mame_fclose(*file);
+ }
+
+ /* create the final file */
+ filerr = mame_fopen(pathoption, fname, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, file);
+
+ /* free the name and get out */
+ free(fname);
+ return filerr;
+}
+
+
+
+/***************************************************************************
+ MNG MOVIE RECORDING
+***************************************************************************/
+
+/*-------------------------------------------------
+ video_is_movie_active - return true if a movie
+ is currently being recorded
+-------------------------------------------------*/
+
+int video_is_movie_active(running_machine *machine, int scrnum)
+{
+ video_private *viddata = machine->video_data;
+ internal_screen_info *info = &viddata->scrinfo[scrnum];
+ return (info->movie_file != NULL);
+}
+
+
+/*-------------------------------------------------
+ video_movie_begin_recording - begin recording
+ of a MNG movie
+-------------------------------------------------*/
+
+void video_movie_begin_recording(running_machine *machine, int scrnum, const char *name)
+{
+ video_private *viddata = machine->video_data;
+ internal_screen_info *info = &viddata->scrinfo[scrnum];
+ file_error filerr;
+
+ /* close any existing movie file */
+ if (info->movie_file != NULL)
+ video_movie_end_recording(machine, scrnum);
+
+ /* create a new movie file and start recording */
+ if (name != NULL)
+ filerr = mame_fopen(SEARCHPATH_MOVIE, name, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &info->movie_file);
+ else
+ filerr = mame_fopen_next(SEARCHPATH_MOVIE, "mng", &info->movie_file);
+ info->movie_frame = 0;
+}
+
+
+/*-------------------------------------------------
+ video_movie_end_recording - stop recording of
+ a MNG movie
+-------------------------------------------------*/
+
+void video_movie_end_recording(running_machine *machine, int scrnum)
+{
+ video_private *viddata = machine->video_data;
+ internal_screen_info *info = &viddata->scrinfo[scrnum];
+
+ /* close the file if it exists */
+ if (info->movie_file != NULL)
+ {
+ mng_capture_stop(mame_core_file(info->movie_file));
+ mame_fclose(info->movie_file);
+ info->movie_file = NULL;
+ info->movie_frame = 0;
+ }
+}
+
+
+/*-------------------------------------------------
+ movie_record_frame - record a frame of a
+ movie
+-------------------------------------------------*/
+
+static void movie_record_frame(running_machine *machine, int scrnum)
+{
+ video_private *viddata = machine->video_data;
+ internal_screen_info *info = &viddata->scrinfo[scrnum];
+
+ /* only record if we have a file */
+ if (info->movie_file != NULL)
+ {
+ png_info pnginfo = { 0 };
+ mame_bitmap *bitmap;
+ png_error error;
+
+ profiler_mark(PROFILER_MOVIE_REC);
+
+ /* get the bitmap */
+ bitmap = get_snapshot_bitmap(machine, scrnum);
+ if (bitmap == NULL)
+ return;
+
+ /* track frames */
+ if (info->movie_frame++ == 0)
+ {
+ char text[256];
+
+ /* set up the text fields in the movie info */
+ sprintf(text, APPNAME " %s", build_version);
+ png_add_text(&pnginfo, "Software", text);
+ sprintf(text, "%s %s", machine->gamedrv->manufacturer, machine->gamedrv->description);
+ png_add_text(&pnginfo, "System", text);
+
+ /* start the capture */
+ error = mng_capture_start(mame_core_file(info->movie_file), bitmap, ATTOSECONDS_TO_HZ(viddata->scrinfo[scrnum].state->refresh));
+ if (error != PNGERR_NONE)
+ {
+ png_free(&pnginfo);
+ video_movie_end_recording(machine, scrnum);
+ return;
+ }
+ }
+
+ /* write the next frame */
+ error = mng_capture_frame(mame_core_file(info->movie_file), &pnginfo, bitmap, machine->drv->total_colors, palette_entry_list_adjusted(machine->palette));
+ png_free(&pnginfo);
+ if (error != PNGERR_NONE)
+ {
+ video_movie_end_recording(machine, scrnum);
+ return;
+ }
+
+ profiler_mark(PROFILER_END);
+ }
+}
+
+
+
+/***************************************************************************
+ CROSSHAIR RENDERING
+***************************************************************************/
+
+/* decripton of the bitmap data */
+#define CROSSHAIR_RAW_SIZE 100
+#define CROSSHAIR_RAW_ROWBYTES ((CROSSHAIR_RAW_SIZE + 7) / 8)
+
+/* raw bitmap */
+static const UINT8 crosshair_raw_top[] =
+{
+ 0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,
+ 0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,
+ 0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xF0,0x00,
+ 0x01,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xF8,0x00,
+ 0x03,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xFC,0x00,
+ 0x07,0xFE,0x00,0x00,0x00,0x0F,0xFE,0x00,0x00,0x00,0x07,0xFE,0x00,
+ 0x0F,0xFF,0x00,0x00,0x01,0xFF,0xFF,0xF0,0x00,0x00,0x0F,0xFF,0x00,
+ 0x1F,0xFF,0x80,0x00,0x1F,0xFF,0xFF,0xFF,0x00,0x00,0x1F,0xFF,0x80,
+ 0x3F,0xFF,0x80,0x00,0xFF,0xFF,0xFF,0xFF,0xE0,0x00,0x1F,0xFF,0xC0,
+ 0x7F,0xFF,0xC0,0x03,0xFF,0xFF,0xFF,0xFF,0xF8,0x00,0x3F,0xFF,0xE0,
+ 0xFF,0xFF,0xE0,0x07,0xFF,0xFF,0xFF,0xFF,0xFC,0x00,0x7F,0xFF,0xF0,
+ 0x7F,0xFF,0xF0,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xE0,
+ 0x3F,0xFF,0xF8,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xC1,0xFF,0xFF,0xC0,
+ 0x0F,0xFF,0xF8,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE1,0xFF,0xFF,0x00,
+ 0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xFE,0x00,
+ 0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,0x00,
+ 0x01,0xFF,0xFF,0xFF,0xFF,0xF0,0x01,0xFF,0xFF,0xFF,0xFF,0xF8,0x00,
+ 0x00,0x7F,0xFF,0xFF,0xFF,0x00,0x00,0x1F,0xFF,0xFF,0xFF,0xE0,0x00,
+ 0x00,0x3F,0xFF,0xFF,0xF8,0x00,0x00,0x03,0xFF,0xFF,0xFF,0xC0,0x00,
+ 0x00,0x1F,0xFF,0xFF,0xE0,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x80,0x00,
+ 0x00,0x0F,0xFF,0xFF,0x80,0x00,0x00,0x00,0x3F,0xFF,0xFF,0x00,0x00,
+ 0x00,0x03,0xFF,0xFE,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFC,0x00,0x00,
+ 0x00,0x01,0xFF,0xFC,0x00,0x00,0x00,0x00,0x07,0xFF,0xF8,0x00,0x00,
+ 0x00,0x03,0xFF,0xF8,0x00,0x00,0x00,0x00,0x01,0xFF,0xF8,0x00,0x00,
+ 0x00,0x07,0xFF,0xFC,0x00,0x00,0x00,0x00,0x03,0xFF,0xFC,0x00,0x00,
+ 0x00,0x0F,0xFF,0xFE,0x00,0x00,0x00,0x00,0x07,0xFF,0xFE,0x00,0x00,
+ 0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFE,0x00,0x00,
+ 0x00,0x1F,0xFF,0xFF,0x80,0x00,0x00,0x00,0x1F,0xFF,0xFF,0x00,0x00,
+ 0x00,0x1F,0xFF,0xFF,0x80,0x00,0x00,0x00,0x1F,0xFF,0xFF,0x00,0x00,
+ 0x00,0x3F,0xFE,0xFF,0xC0,0x00,0x00,0x00,0x3F,0xFF,0xFF,0x80,0x00,
+ 0x00,0x7F,0xFC,0x7F,0xE0,0x00,0x00,0x00,0x7F,0xE7,0xFF,0xC0,0x00,
+ 0x00,0x7F,0xF8,0x3F,0xF0,0x00,0x00,0x00,0xFF,0xC3,0xFF,0xC0,0x00,
+ 0x00,0xFF,0xF8,0x1F,0xF8,0x00,0x00,0x01,0xFF,0x83,0xFF,0xE0,0x00,
+ 0x00,0xFF,0xF0,0x07,0xF8,0x00,0x00,0x01,0xFE,0x01,0xFF,0xE0,0x00,
+ 0x00,0xFF,0xF0,0x03,0xFC,0x00,0x00,0x03,0xFC,0x01,0xFF,0xE0,0x00,
+ 0x01,0xFF,0xE0,0x01,0xFE,0x00,0x00,0x07,0xF8,0x00,0xFF,0xF0,0x00,
+ 0x01,0xFF,0xE0,0x00,0xFF,0x00,0x00,0x0F,0xF0,0x00,0xFF,0xF0,0x00,
+ 0x01,0xFF,0xC0,0x00,0x3F,0x80,0x00,0x1F,0xC0,0x00,0x7F,0xF0,0x00,
+ 0x01,0xFF,0xC0,0x00,0x1F,0x80,0x00,0x1F,0x80,0x00,0x7F,0xF0,0x00,
+ 0x03,0xFF,0xC0,0x00,0x0F,0xC0,0x00,0x3F,0x00,0x00,0x7F,0xF8,0x00,
+ 0x03,0xFF,0x80,0x00,0x07,0xE0,0x00,0x7E,0x00,0x00,0x3F,0xF8,0x00,
+ 0x03,0xFF,0x80,0x00,0x01,0xF0,0x00,0xF8,0x00,0x00,0x3F,0xF8,0x00,
+ 0x03,0xFF,0x80,0x00,0x00,0xF8,0x01,0xF0,0x00,0x00,0x3F,0xF8,0x00,
+ 0x03,0xFF,0x80,0x00,0x00,0x78,0x01,0xE0,0x00,0x00,0x3F,0xF8,0x00,
+ 0x07,0xFF,0x00,0x00,0x00,0x3C,0x03,0xC0,0x00,0x00,0x3F,0xFC,0x00,
+ 0x07,0xFF,0x00,0x00,0x00,0x0E,0x07,0x00,0x00,0x00,0x1F,0xFC,0x00,
+ 0x07,0xFF,0x00,0x00,0x00,0x07,0x0E,0x00,0x00,0x00,0x1F,0xFC,0x00,
+ 0x07,0xFF,0x00,0x00,0x00,0x03,0x9C,0x00,0x00,0x00,0x1F,0xFC,0x00,
+ 0x07,0xFF,0x00,0x00,0x00,0x01,0x98,0x00,0x00,0x00,0x1F,0xFC,0x00,
+ 0x07,0xFF,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x1F,0xFC,0x00
+};
+
+/* per-player colors */
+static const rgb_t crosshair_colors[] =
+{
+ MAKE_RGB(0x40,0x40,0xff),
+ MAKE_RGB(0xff,0x40,0x40),
+ MAKE_RGB(0x40,0xff,0x40),
+ MAKE_RGB(0xff,0xff,0x40),
+ MAKE_RGB(0xff,0x40,0xff),
+ MAKE_RGB(0x40,0xff,0xff),
+ MAKE_RGB(0xff,0xff,0xff)
+};
+
+
+/*-------------------------------------------------
+ crosshair_init - initialize the crosshair
+ bitmaps and such
+-------------------------------------------------*/
+
+static void crosshair_init(video_private *viddata)
+{
+ input_port_entry *ipt;
+ int player;
+
+ /* determine who needs crosshairs */
+ viddata->crosshair_needed = 0x00;
+ for (ipt = Machine->input_ports; ipt->type != IPT_END; ipt++)
+ if (ipt->analog.crossaxis != CROSSHAIR_AXIS_NONE)
+ viddata->crosshair_needed |= 1 << ipt->player;
+
+ /* all visible by default */
+ viddata->crosshair_visible = viddata->crosshair_needed;
+
+ /* loop over each player and load or create a bitmap */
+ for (player = 0; player < MAX_PLAYERS; player++)
+ if (viddata->crosshair_needed & (1 << player))
+ {
+ char filename[20];
+
+ /* first try to load a bitmap for the crosshair */
+ sprintf(filename, "cross%d.png", player);
+ viddata->crosshair_bitmap[player] = render_load_png(NULL, filename, NULL, NULL);
+
+ /* if that didn't work, make one up */
+ if (viddata->crosshair_bitmap[player] == NULL)
+ {
+ rgb_t color = crosshair_colors[player];
+ int x, y;
+
+ /* allocate a blank bitmap to start with */
+ viddata->crosshair_bitmap[player] = bitmap_alloc(CROSSHAIR_RAW_SIZE, CROSSHAIR_RAW_SIZE, BITMAP_FORMAT_ARGB32);
+ fillbitmap(viddata->crosshair_bitmap[player], MAKE_ARGB(0x00,0xff,0xff,0xff), NULL);
+
+ /* extract the raw source data to it */
+ for (y = 0; y < CROSSHAIR_RAW_SIZE / 2; y++)
+ {
+ /* assume it is mirrored vertically */
+ UINT32 *dest0 = BITMAP_ADDR32(viddata->crosshair_bitmap[player], y, 0);
+ UINT32 *dest1 = BITMAP_ADDR32(viddata->crosshair_bitmap[player], CROSSHAIR_RAW_SIZE - 1 - y, 0);
+
+ /* extract to two rows simultaneously */
+ for (x = 0; x < CROSSHAIR_RAW_SIZE; x++)
+ if ((crosshair_raw_top[y * CROSSHAIR_RAW_ROWBYTES + x / 8] << (x % 8)) & 0x80)
+ dest0[x] = dest1[x] = MAKE_ARGB(0xff,0x00,0x00,0x00) | color;
+ }
+ }
+
+ /* create a texture to reference the bitmap */
+ viddata->crosshair_texture[player] = render_texture_alloc(render_texture_hq_scale, NULL);
+ render_texture_set_bitmap(viddata->crosshair_texture[player], viddata->crosshair_bitmap[player], NULL, 0, TEXFORMAT_ARGB32);
+ }
+}
+
+
+/*-------------------------------------------------
+ video_crosshair_toggle - toggle crosshair
+ visibility
+-------------------------------------------------*/
+
+void video_crosshair_toggle(void)
+{
+ video_private *viddata = Machine->video_data;
+ int player;
+
+ /* if we're all visible, turn all off */
+ if (viddata->crosshair_visible == viddata->crosshair_needed)
+ viddata->crosshair_visible = 0;
+
+ /* otherwise, turn on the first bit that isn't currently on and stop there */
+ else
+ for (player = 0; player < MAX_PLAYERS; player++)
+ if ((viddata->crosshair_needed & (1 << player)) && !(viddata->crosshair_visible & (1 << player)))
+ {
+ viddata->crosshair_visible |= 1 << player;
+ break;
+ }
+}
+
+
+/*-------------------------------------------------
+ get_crosshair_screen_mask - returns a bitmask
+ indicating on which screens the crosshair for
+ a player's should be displayed
+-------------------------------------------------*/
+
+static UINT32 get_crosshair_screen_mask(video_private *viddata, int player)
+{
+ return (viddata->crosshair_visible & (1 << player)) ? 1 : 0;
+}
+
+
+/*-------------------------------------------------
+ crosshair_render - render the crosshairs
+-------------------------------------------------*/
+
+static void crosshair_render(video_private *viddata)
+{
+ float x[MAX_PLAYERS], y[MAX_PLAYERS];
+ input_port_entry *ipt;
+ int portnum = -1;
+ int player;
+ UINT8 tscale;
+
+ /* skip if not needed */
+ if (viddata->crosshair_visible == 0)
+ return;
+
+ /* animate via crosshair_animate */
+ viddata->crosshair_animate += 0x04;
+
+ /* compute a color scaling factor from the current animation value */
+ if (viddata->crosshair_animate < 0x80)
+ tscale = 0xa0 + (0x60 * ( viddata->crosshair_animate & 0x7f) / 0x80);
+ else
+ tscale = 0xa0 + (0x60 * (~viddata->crosshair_animate & 0x7f) / 0x80);
+
+ /* read all the lightgun values */
+ for (ipt = Machine->input_ports; ipt->type != IPT_END; ipt++)
+ {
+ /* keep track of the port number */
+ if (ipt->type == IPT_PORT)
+ portnum++;
+
+ /* compute the values */
+ if (ipt->analog.crossaxis != CROSSHAIR_AXIS_NONE)
+ {
+ float value = (float)(get_crosshair_pos(portnum, ipt->player, ipt->analog.crossaxis) - ipt->analog.min) / (float)(ipt->analog.max - ipt->analog.min);
+ if (ipt->analog.crossscale < 0)
+ value = -(1.0 - value) * ipt->analog.crossscale;
+ else
+ value *= ipt->analog.crossscale;
+ value += ipt->analog.crossoffset;
+
+ /* switch off the axis */
+ switch (ipt->analog.crossaxis)
+ {
+ case CROSSHAIR_AXIS_X:
+ x[ipt->player] = value;
+ if (ipt->analog.crossaltaxis != 0)
+ y[ipt->player] = ipt->analog.crossaltaxis;
+ break;
+
+ case CROSSHAIR_AXIS_Y:
+ y[ipt->player] = value;
+ if (ipt->analog.crossaltaxis != 0)
+ x[ipt->player] = ipt->analog.crossaltaxis;
+ break;
+ }
+ }
+ }
+
+ /* draw all crosshairs */
+ for (player = 0; player < MAX_PLAYERS; player++)
+ {
+ UINT32 scrmask = get_crosshair_screen_mask(viddata, player);
+ if (scrmask != 0)
+ {
+ int scrnum;
+
+ for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++)
+ {
+ if (scrmask & (1 << scrnum))
+ {
+ /* add a quad assuming a 4:3 screen (this is not perfect) */
+ render_screen_add_quad(scrnum,
+ x[player] - 0.03f, y[player] - 0.04f,
+ x[player] + 0.03f, y[player] + 0.04f,
+ MAKE_ARGB(0xc0, tscale, tscale, tscale),
+ viddata->crosshair_texture[player], PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ }
+ }
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ crosshair_free - free memory allocated for
+ the crosshairs
+-------------------------------------------------*/
+
+static void crosshair_free(video_private *viddata)
+{
+ int player;
+
+ /* free bitmaps and textures for each player */
+ for (player = 0; player < MAX_PLAYERS; player++)
+ {
+ if (viddata->crosshair_texture[player] != NULL)
+ render_texture_free(viddata->crosshair_texture[player]);
+ viddata->crosshair_texture[player] = NULL;
+
+ if (viddata->crosshair_bitmap[player] != NULL)
+ bitmap_free(viddata->crosshair_bitmap[player]);
+ viddata->crosshair_bitmap[player] = NULL;
+ }
+}
+
+
+
+/***************************************************************************
+ SOFTWARE RENDERING
+***************************************************************************/
+
+#define FUNC_PREFIX(x) rgb888_##x
+#define PIXEL_TYPE UINT32
+#define SRCSHIFT_R 0
+#define SRCSHIFT_G 0
+#define SRCSHIFT_B 0
+#define DSTSHIFT_R 16
+#define DSTSHIFT_G 8
+#define DSTSHIFT_B 0
+
+#include "rendersw.c"