summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author AJR <ajrhacker@users.noreply.github.com>2018-09-25 20:41:57 -0400
committer AJR <ajrhacker@users.noreply.github.com>2018-09-25 20:41:57 -0400
commitf1e5b8960adf7680bb74087389cec0f7a0e75753 (patch)
treee0774617bdb806e3e7d5c1f3ae035110c99f4026
parent7f7ec353bfa77b75de79b994ffa3d38bbeb97d43 (diff)
dp8350: More timing outputs (nw)
-rw-r--r--src/devices/video/dp8350.cpp136
-rw-r--r--src/devices/video/dp8350.h22
-rw-r--r--src/mame/drivers/hazl1420.cpp5
3 files changed, 138 insertions, 25 deletions
diff --git a/src/devices/video/dp8350.cpp b/src/devices/video/dp8350.cpp
index 7fbb1dd5978..74bdb7653b2 100644
--- a/src/devices/video/dp8350.cpp
+++ b/src/devices/video/dp8350.cpp
@@ -72,20 +72,15 @@ dp835x_device::dp835x_device(const machine_config &mconfig, device_type type, co
, m_hsync_active(hsync_active)
, m_vsync_active(vsync_active)
, m_vblank_active(vblank_active)
+ , m_hsync_callback(*this)
+ , m_vsync_callback(*this)
, m_vblank_callback(*this)
, m_60hz_refresh(true)
{
- // many parameters are not used yet
- (void)m_vsync_delay;
- (void)m_vsync_width;
- (void)m_hsync_delay;
- (void)m_hsync_width;
- (void)m_vblank_stop;
+ // some parameters are not used yet
(void)m_cursor_on_all_lines;
(void)m_lbc_0_width;
(void)m_hsync_serration;
- (void)m_hsync_active;
- (void)m_vsync_active;
}
@@ -150,8 +145,8 @@ void dp835x_device::device_config_complete()
void dp835x_device::device_resolve_objects()
{
- //m_hsync_callback.resolve_safe();
- //m_vsync_callback.resolve_safe();
+ m_hsync_callback.resolve_safe();
+ m_vsync_callback.resolve_safe();
m_vblank_callback.resolve_safe();
}
@@ -162,8 +157,15 @@ void dp835x_device::device_resolve_objects()
void dp835x_device::device_start()
{
- screen().register_vblank_callback(vblank_state_delegate(&dp835x_device::screen_vblank, this));
-
+ // create timers
+ m_hsync_on_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::hsync_update), this));
+ m_hsync_off_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::hsync_update), this));
+ m_vsync_on_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::vsync_update), this));
+ m_vsync_off_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::vsync_update), this));
+ m_vblank_on_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::vblank_update), this));
+ m_vblank_off_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::vblank_update), this));
+
+ // save state
save_item(NAME(m_60hz_refresh));
}
@@ -197,25 +199,67 @@ void dp835x_device::reconfigure_screen()
int dots_per_line = m_char_width * m_chars_per_line;
int dots_per_row = m_char_width * m_chars_per_row;
int lines_per_frame = m_char_height * m_rows_per_frame + m_vblank_interval[m_60hz_refresh ? 1 : 0];
- attoseconds_t refresh = clocks_to_attotime(lines_per_frame * dots_per_line).as_attoseconds();
+ attotime refresh = clocks_to_attotime(lines_per_frame * dots_per_line);
if (m_half_shift)
{
rectangle visarea(0, 2 * dots_per_row - 1, 0, m_char_height * m_rows_per_frame - 1);
- screen().configure(2 * dots_per_line, lines_per_frame, visarea, refresh);
+ screen().configure(2 * dots_per_line, lines_per_frame, visarea, refresh.as_attoseconds());
}
else
{
rectangle visarea(0, dots_per_row - 1, 0, m_char_height * m_rows_per_frame - 1);
- screen().configure(dots_per_line, lines_per_frame, visarea, refresh);
+ screen().configure(dots_per_line, lines_per_frame, visarea, refresh.as_attoseconds());
}
logerror("Frame rate refresh: %.2f Hz (f%d); horizontal rate scan: %.4f kHz; character rate: %.4f MHz; dot rate: %.5f MHz\n",
- ATTOSECONDS_TO_HZ(refresh),
+ ATTOSECONDS_TO_HZ(refresh.as_attoseconds()),
m_60hz_refresh ? 1 : 0,
clock() / (dots_per_line * 1000.0),
clock() / (m_char_width * 1000000.0),
clock() / 1000000.0);
+
+ // get current screen position
+ int hpos = screen().hpos();
+ int vpos = screen().vpos();
+
+ // set horizontal sync timers
+ int hsync_begin = (dots_per_row + m_char_width * m_hsync_delay) * (m_half_shift ? 2 : 1);
+ int hsync_end = hsync_begin + m_char_width * m_hsync_width * (m_half_shift ? 2 : 1);
+ if (hpos > hsync_begin)
+ hsync_begin += dots_per_line * (m_half_shift ? 2 : 1);
+ m_hsync_on_timer->adjust(clocks_to_attotime(hsync_begin - hpos) / (m_half_shift ? 2 : 1), m_hsync_active, clocks_to_attotime(dots_per_line));
+ if (hpos > hsync_end)
+ hsync_end += dots_per_line * (m_half_shift ? 2 : 1);
+ m_hsync_off_timer->adjust(clocks_to_attotime(hsync_end - hpos) / (m_half_shift ? 2 : 1), !m_hsync_active, clocks_to_attotime(dots_per_line));
+
+ // calculate vertical sync and blanking parameters
+ int hblank_begin = dots_per_row * (m_half_shift ? 2 : 1);
+ int vblank_begin = m_char_height * m_rows_per_frame - 1;
+ int vsync_begin = vblank_begin + m_vsync_delay[m_60hz_refresh ? 1 : 0];
+ int vsync_end = vsync_begin + m_vsync_width[m_60hz_refresh ? 1 : 0];
+ int vblank_end = lines_per_frame - m_vblank_stop - 1;
+ logerror("vblank_begin: %d; vsync_begin: %d; vsync_end: %d; vblank_end: %d; vpos: %d\n", vblank_begin, vsync_begin, vsync_end, vblank_end, vpos);
+ if (hpos > hblank_begin)
+ {
+ hblank_begin += dots_per_line * (m_half_shift ? 2 : 1);
+ vpos++;
+ }
+ attotime until_hblank = clocks_to_attotime(hblank_begin - hpos) / (m_half_shift ? 2 : 1);
+
+ // set vertical sync and blanking timers
+ if (vpos > vsync_begin)
+ vsync_begin += lines_per_frame;
+ m_vsync_on_timer->adjust(clocks_to_attotime((vsync_begin - vpos) * dots_per_line) + until_hblank, m_vsync_active, refresh);
+ if (vpos > vsync_end)
+ vsync_end += lines_per_frame;
+ m_vsync_off_timer->adjust(clocks_to_attotime((vsync_end - vpos) * dots_per_line) + until_hblank, !m_vsync_active, refresh);
+ if (vpos > vblank_begin)
+ vblank_begin += lines_per_frame;
+ m_vblank_on_timer->adjust(clocks_to_attotime((vblank_begin - vpos) * dots_per_line) + until_hblank, m_vblank_active, refresh);
+ if (vpos > vblank_end)
+ vblank_end += lines_per_frame;
+ m_vblank_off_timer->adjust(clocks_to_attotime((vblank_end - vpos) * dots_per_line) + until_hblank, !m_vblank_active, refresh);
}
@@ -270,20 +314,72 @@ void dp835x_device::register_load(u8 rs, u16 addr)
//-------------------------------------------------
+// hsync_r - report horizontal sync state
+//-------------------------------------------------
+
+READ_LINE_MEMBER(dp835x_device::hsync_r)
+{
+ if (m_hsync_on_timer->remaining() > m_hsync_off_timer->remaining())
+ return m_hsync_active;
+ else
+ return !m_hsync_active;
+}
+
+
+//-------------------------------------------------
+// vblank_r - report vertical sync state
+//-------------------------------------------------
+
+READ_LINE_MEMBER(dp835x_device::vsync_r)
+{
+ if (m_vsync_on_timer->remaining() > m_vsync_off_timer->remaining())
+ return m_vsync_active;
+ else
+ return !m_vsync_active;
+}
+
+
+//-------------------------------------------------
// vblank_r - report vertical blanking state
//-------------------------------------------------
READ_LINE_MEMBER(dp835x_device::vblank_r)
{
- return bool(screen().vblank()) == m_vblank_active;
+ if (m_vblank_on_timer->remaining() > m_vblank_off_timer->remaining())
+ return m_vblank_active;
+ else
+ return !m_vblank_active;
+}
+
+
+//-------------------------------------------------
+// hsync_update - update state of horizontal
+// sync output
+//-------------------------------------------------
+
+TIMER_CALLBACK_MEMBER(dp835x_device::hsync_update)
+{
+ m_hsync_callback(param);
+}
+
+
+//-------------------------------------------------
+// vsync_update - update state of vertical
+// sync output
+//-------------------------------------------------
+
+TIMER_CALLBACK_MEMBER(dp835x_device::vsync_update)
+{
+ m_vsync_callback(param);
}
//-------------------------------------------------
-// screen_vblank - vertical blanking handler
+// vblank_update - update state of vertical
+// blanking output
//-------------------------------------------------
-void dp835x_device::screen_vblank(screen_device &screen, bool state)
+TIMER_CALLBACK_MEMBER(dp835x_device::vblank_update)
{
- m_vblank_callback(state == m_vblank_active);
+ m_vblank_callback(param);
}
diff --git a/src/devices/video/dp8350.h b/src/devices/video/dp8350.h
index c14adf05a1b..4ee885540a7 100644
--- a/src/devices/video/dp8350.h
+++ b/src/devices/video/dp8350.h
@@ -44,6 +44,8 @@ class dp835x_device : public device_t, public device_video_interface
{
public:
// device configuration
+ auto hsync_callback() { return m_hsync_callback.bind(); }
+ auto vsync_callback() { return m_vsync_callback.bind(); }
auto vblank_callback() { return m_vblank_callback.bind(); }
void set_half_shift(bool half_shift) { m_half_shift = half_shift; }
@@ -52,6 +54,8 @@ public:
void register_load(u8 rs, u16 addr);
// read handlers
+ DECLARE_READ_LINE_MEMBER(hsync_r);
+ DECLARE_READ_LINE_MEMBER(vsync_r);
DECLARE_READ_LINE_MEMBER(vblank_r);
protected:
@@ -74,7 +78,11 @@ protected:
private:
// internal helpers
void reconfigure_screen();
- void screen_vblank(screen_device &screen, bool state);
+
+ // timer callbacks
+ TIMER_CALLBACK_MEMBER(hsync_update);
+ TIMER_CALLBACK_MEMBER(vsync_update);
+ TIMER_CALLBACK_MEMBER(vblank_update);
// mask parameters
const int m_char_width; // character field cell size (width in dots)
@@ -99,12 +107,20 @@ private:
bool m_half_shift; // adjust screen parameters to allow half-dot shifting
// device callbacks
- //devcb_write_line m_hsync_callback; // horizontal sync output (polarity may vary by type)
- //devcb_write_line m_vsync_callback; // vertical sync output (polarity may vary by type)
+ devcb_write_line m_hsync_callback; // horizontal sync output (polarity may vary by type)
+ devcb_write_line m_vsync_callback; // vertical sync output (polarity may vary by type)
devcb_write_line m_vblank_callback; // vertical blanking output (polarity may vary by type)
// internal registers and control parameters
bool m_60hz_refresh; // refresh rate selector (true = f1, false = f0)
+
+ // timers
+ emu_timer *m_hsync_on_timer;
+ emu_timer *m_hsync_off_timer;
+ emu_timer *m_vsync_on_timer;
+ emu_timer *m_vsync_off_timer;
+ emu_timer *m_vblank_on_timer;
+ emu_timer *m_vblank_off_timer;
};
// ======================> dp8350_device
diff --git a/src/mame/drivers/hazl1420.cpp b/src/mame/drivers/hazl1420.cpp
index a3ce1410f88..103fc954e0c 100644
--- a/src/mame/drivers/hazl1420.cpp
+++ b/src/mame/drivers/hazl1420.cpp
@@ -63,7 +63,7 @@ void hazl1420_state::p1_w(u8 data)
u8 hazl1420_state::p2_r()
{
- u8 result = 0xe0 | (m_crtc->vblank_r() << 4);
+ u8 result = 0xe0 | (m_crtc->hsync_r() << 4);
result |= m_ioexp[0]->p2_r() & m_ioexp[1]->p2_r();
return result;
}
@@ -293,6 +293,7 @@ void hazl1420_state::hazl1420(machine_config &config)
m_maincpu->p2_out_cb().append(m_ioexp[1], FUNC(i8243_device::p2_w));
m_maincpu->prog_out_cb().set(m_ioexp[0], FUNC(i8243_device::prog_w));
m_maincpu->prog_out_cb().append(m_ioexp[1], FUNC(i8243_device::prog_w));
+ m_maincpu->t0_in_cb().set(m_crtc, FUNC(dp8350_device::vblank_r));
m_maincpu->t1_in_cb().set("ace", FUNC(ins8250_device::intrpt_r));
ADDRESS_MAP_BANK(config, m_bankdev);
@@ -315,7 +316,7 @@ void hazl1420_state::hazl1420(machine_config &config)
INS8250(config, "ace", 2'764'800);
DP8350(config, m_crtc, 10.92_MHz_XTAL).set_screen("screen");
- m_crtc->vblank_callback().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
+ m_crtc->hsync_callback().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
screen.set_screen_update(FUNC(hazl1420_state::screen_update));
eredoc */ .highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ .highlight .sx { color: #4e9a06 } /* Literal.String.Other */ .highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ .highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ .highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ .highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #000000 } /* Name.Function.Magic */ .highlight .vc { color: #000000 } /* Name.Variable.Class */ .highlight .vg { color: #000000 } /* Name.Variable.Global */ .highlight .vi { color: #000000 } /* Name.Variable.Instance */ .highlight .vm { color: #000000 } /* Name.Variable.Magic */ .highlight .il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */
/***************************************************************************

    driver.c

    Driver construction helpers.

    Copyright Nicola Salmoria and the MAME Team.
    Visit http://mamedev.org for licensing and usage restrictions.

***************************************************************************/

#include "driver.h"
#include <ctype.h>



/***************************************************************************
    CONSTANTS
***************************************************************************/

#define DRIVER_LRU_SIZE			10



/***************************************************************************
    GLOBAL VARIABLES
***************************************************************************/

static int driver_lru[DRIVER_LRU_SIZE];



/***************************************************************************
    FUNCTION PROTOTYPES
***************************************************************************/

static int penalty_compare(const char *source, const char *target);



/***************************************************************************
    MISCELLANEOUS BITS & PIECES
***************************************************************************/

/*-------------------------------------------------
    expand_machine_driver - construct a machine
    driver from the macroized state
-------------------------------------------------*/

void expand_machine_driver(void (*constructor)(machine_config *), machine_config *output)
{
	/* initialize the tag on the first screen */
	memset(output, 0, sizeof(*output));
	output->watchdog_time = attotime_zero;

	/* keeping this function allows us to pre-init the driver before constructing it */
	(*constructor)(output);

	/* if no screen tagged, tag screen 0 as main */
	if (output->screen[0].tag == NULL && output->screen[0].defstate.format != BITMAP_FORMAT_INVALID)
		output->screen[0].tag = "main";

	/* if no screens, set a dummy refresh for the main screen */
	if (output->screen[0].tag == NULL)
		output->screen[0].defstate.refresh = HZ_TO_ATTOSECONDS(60);
}


/*-------------------------------------------------
    driver_add_cpu - add a CPU during machine
    driver expansion
-------------------------------------------------*/

cpu_config *driver_add_cpu(machine_config *machine, const char *tag, cpu_type type, int cpuclock)
{
	int cpunum;

	for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
		if (machine->cpu[cpunum].type == CPU_DUMMY)
		{
			machine->cpu[cpunum].tag = tag;
			machine->cpu[cpunum].type = type;
			machine->cpu[cpunum].clock = cpuclock;
			return &machine->cpu[cpunum];
		}

	fatalerror("Out of CPU's!\n");
	return NULL;
}


/*-------------------------------------------------
    driver_find_cpu - find a tagged CPU during
    machine driver expansion
-------------------------------------------------*/

cpu_config *driver_find_cpu(machine_config *machine, const char *tag)
{
	int cpunum;

	for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
		if (machine->cpu[cpunum].tag && strcmp(machine->cpu[cpunum].tag, tag) == 0)
			return &machine->cpu[cpunum];

	fatalerror("Can't find CPU '%s'!\n", tag);
	return NULL;
}


/*-------------------------------------------------
    driver_remove_cpu - remove a tagged CPU
    during machine driver expansion
-------------------------------------------------*/

void driver_remove_cpu(machine_config *machine, const char *tag)
{
	int cpunum;

	for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
		if (machine->cpu[cpunum].tag && strcmp(machine->cpu[cpunum].tag, tag) == 0)
		{
			memmove(&machine->cpu[cpunum], &machine->cpu[cpunum + 1], sizeof(machine->cpu[0]) * (MAX_CPU - cpunum - 1));
			memset(&machine->cpu[MAX_CPU - 1], 0, sizeof(machine->cpu[0]));
			return;
		}

	fatalerror("Can't find CPU '%s'!\n", tag);
}


/*-------------------------------------------------
    driver_add_speaker - add a speaker during
    machine driver expansion
-------------------------------------------------*/

speaker_config *driver_add_speaker(machine_config *machine, const char *tag, float x, float y, float z)
{
	int speakernum;

	for (speakernum = 0; speakernum < MAX_SPEAKER; speakernum++)
		if (machine->speaker[speakernum].tag == NULL)
		{
			machine->speaker[speakernum].tag = tag;
			machine->speaker[speakernum].x = x;
			machine->speaker[speakernum].y = y;
			machine->speaker[speakernum].z = z;
			return &machine->speaker[speakernum];
		}

	fatalerror("Out of speakers!\n");
	return NULL;
}


/*-------------------------------------------------
    driver_find_speaker - find a tagged speaker
    system during machine driver expansion
-------------------------------------------------*/

speaker_config *driver_find_speaker(machine_config *machine, const char *tag)
{
	int speakernum;

	for (speakernum = 0; speakernum < MAX_SPEAKER; speakernum++)
		if (machine->speaker[speakernum].tag && strcmp(machine->speaker[speakernum].tag, tag) == 0)
			return &machine->speaker[speakernum];

	fatalerror("Can't find speaker '%s'!\n", tag);
	return NULL;
}


/*-------------------------------------------------
    driver_remove_speaker - remove a tagged speaker
    system during machine driver expansion
-------------------------------------------------*/

void driver_remove_speaker(machine_config *machine, const char *tag)
{
	int speakernum;

	for (speakernum = 0; speakernum < MAX_SPEAKER; speakernum++)
		if (machine->speaker[speakernum].tag && strcmp(machine->speaker[speakernum].tag, tag) == 0)
		{
			memmove(&machine->speaker[speakernum], &machine->speaker[speakernum + 1], sizeof(machine->speaker[0]) * (MAX_SPEAKER - speakernum - 1));
			memset(&machine->speaker[MAX_SPEAKER - 1], 0, sizeof(machine->speaker[0]));
			return;
		}

	fatalerror("Can't find speaker '%s'!\n", tag);
}


/*-------------------------------------------------
    driver_add_sound - add a sound system during
    machine driver expansion
-------------------------------------------------*/

sound_config *driver_add_sound(machine_config *machine, const char *tag, sound_type type, int clock)
{
	int soundnum;

	for (soundnum = 0; soundnum < MAX_SOUND; soundnum++)
		if (machine->sound[soundnum].type == SOUND_DUMMY)
		{
			machine->sound[soundnum].tag = tag;
			machine->sound[soundnum].type = type;
			machine->sound[soundnum].clock = clock;
			machine->sound[soundnum].config = NULL;
			machine->sound[soundnum].routes = 0;
			return &machine->sound[soundnum];
		}

	fatalerror("Out of sounds!\n");
	return NULL;
}


/*-------------------------------------------------
    driver_find_sound - find a tagged sound
    system during machine driver expansion
-------------------------------------------------*/

sound_config *driver_find_sound(machine_config *machine, const char *tag)
{
	int soundnum;

	for (soundnum = 0; soundnum < MAX_SOUND; soundnum++)
		if (machine->sound[soundnum].tag && strcmp(machine->sound[soundnum].tag, tag) == 0)
			return &machine->sound[soundnum];

	fatalerror("Can't find sound '%s'!\n", tag);
	return NULL;
}


/*-------------------------------------------------
    driver_remove_sound - remove a tagged sound
    system during machine driver expansion
-------------------------------------------------*/

void driver_remove_sound(machine_config *machine, const char *tag)
{
	int soundnum;

	for (soundnum = 0; soundnum < MAX_SOUND; soundnum++)
		if (machine->sound[soundnum].tag && strcmp(machine->sound[soundnum].tag, tag) == 0)
		{
			memmove(&machine->sound[soundnum], &machine->sound[soundnum + 1], sizeof(machine->sound[0]) * (MAX_SOUND - soundnum - 1));
			memset(&machine->sound[MAX_SOUND - 1], 0, sizeof(machine->sound[0]));
			return;
		}

	fatalerror("Can't find sound '%s'!\n", tag);
}


/*-------------------------------------------------
    driver_add_screen - add a screen during
    machine driver expansion
-------------------------------------------------*/

screen_config *driver_add_screen(machine_config *machine, const char *tag, int palbase)
{
	int screennum;

	for (screennum = 0; screennum < MAX_SCREENS; screennum++)
		if (machine->screen[screennum].tag == NULL)
		{
			machine->screen[screennum].tag = tag;
			machine->screen[screennum].palette_base = palbase;
			return &machine->screen[screennum];
		}

	fatalerror("Out of screens!\n");
	return NULL;
}


/*-------------------------------------------------
    driver_find_screen - find a tagged screen
    during machine driver expansion
-------------------------------------------------*/

screen_config *driver_find_screen(machine_config *machine, const char *tag)
{
	int screennum;

	for (screennum = 0; screennum < MAX_SCREENS; screennum++)
		if (machine->screen[screennum].tag && strcmp(machine->screen[screennum].tag, tag) == 0)
			return &machine->screen[screennum];

	fatalerror("Can't find screen '%s'!\n", tag);
	return NULL;
}


/*-------------------------------------------------
    driver_remove_screen - remove a tagged screen
    during machine driver expansion
-------------------------------------------------*/

void driver_remove_screen(machine_config *machine, const char *tag)
{
	int screennum;

	for (screennum = 0; screennum < MAX_SCREENS; screennum++)
		if (machine->screen[screennum].tag && strcmp(machine->screen[screennum].tag, tag) == 0)
		{
			memmove(&machine->screen[screennum], &machine->screen[screennum + 1], sizeof(machine->screen[0]) * (MAX_SCREENS - screennum - 1));
			memset(&machine->screen[MAX_SCREENS - 1], 0, sizeof(machine->screen[0]));
			return;
		}

	fatalerror("Can't find screen '%s'!\n", tag);
}


/*-------------------------------------------------
    driver_get_name - return a pointer to a
    driver given its name
-------------------------------------------------*/

const game_driver *driver_get_name(const char *name)
{
	int lurnum, drvnum;

	/* scan the LRU list first */
	for (lurnum = 0; lurnum < DRIVER_LRU_SIZE; lurnum++)
		if (mame_stricmp(drivers[driver_lru[lurnum]]->name, name) == 0)
		{
			/* if not first, swap with the head */
			if (lurnum != 0)
			{
				int temp = driver_lru[0];
				driver_lru[0] = driver_lru[lurnum];
				driver_lru[lurnum] = temp;
			}
			return drivers[driver_lru[0]];
		}

	/* scan for a match in the drivers -- slow! */
	for (drvnum = 0; drivers[drvnum] != NULL; drvnum++)
		if (mame_stricmp(drivers[drvnum]->name, name) == 0)
		{
			memmove((void *)&driver_lru[1], (void *)&driver_lru[0], sizeof(driver_lru[0]) * (DRIVER_LRU_SIZE - 1));
			driver_lru[0] = drvnum;
			return drivers[drvnum];
		}

	return NULL;
}


/*-------------------------------------------------
    driver_get_clone - return a pointer to the
    clone of a game driver.
-------------------------------------------------*/

const game_driver *driver_get_clone(const game_driver *driver)
{
	/* if no clone, easy out */
	if (driver->parent == NULL || (driver->parent[0] == '0' && driver->parent[1] == 0))
		return NULL;

	/* convert the name to a game_driver */
	return driver_get_name(driver->parent);
}


/*-------------------------------------------------
    driver_list_get_approx_matches - find the best
    n matches to a driver name.
-------------------------------------------------*/

void driver_list_get_approx_matches(const game_driver * const driverlist[], const char *name, int matches, const game_driver **list)
{
#undef rand

	int matchnum, drvnum;
	int *penalty;

	/* if no name, pick random entries */
	if (name == NULL || name[0] == 0)
	{
		const game_driver **templist;
		int driver_count;
		int shufnum;

		/* allocate a temporary list */
		templist = malloc_or_die(driver_list_get_count(driverlist) * sizeof(*templist));

		/* build up a list of valid entries */
		for (drvnum = driver_count = 0; driverlist[drvnum] != NULL; drvnum++)
			if ((driverlist[drvnum]->flags & GAME_NO_STANDALONE) == 0)
				templist[driver_count++] = driverlist[drvnum];

		/* seed the RNG first */
		srand(osd_ticks());

		/* shuffle */
		for (shufnum = 0; shufnum < 4 * driver_count; shufnum++)
		{
			int item1 = rand() % driver_count;
			int item2 = rand() % driver_count;
			const game_driver *temp;

			temp = templist[item1];
			templist[item1] = templist[item2];
			templist[item2] = temp;
		}

		/* copy out the first few entries */
		for (matchnum = 0; matchnum < matches; matchnum++)
			list[matchnum] = templist[matchnum % driver_count];

		free((void *)templist);
		return;
	}

	/* allocate some temp memory */
	penalty = malloc_or_die(matches * sizeof(*penalty));

	/* initialize everyone's states */
	for (matchnum = 0; matchnum < matches; matchnum++)
	{
		penalty[matchnum] = 9999;
		list[matchnum] = NULL;
	}

	/* scan the entire drivers array */
	for (drvnum = 0; driverlist[drvnum] != NULL; drvnum++)
	{
		int curpenalty, tmp;

		/* skip things that can't run */
		if ((driverlist[drvnum]->flags & GAME_NO_STANDALONE) != 0)
			continue;

		/* pick the best match between driver name and description */
		curpenalty = penalty_compare(name, driverlist[drvnum]->description);
		tmp = penalty_compare(name, driverlist[drvnum]->name);
		curpenalty = MIN(curpenalty, tmp);

		/* insert into the sorted table of matches */
		for (matchnum = matches - 1; matchnum >= 0; matchnum--)
		{
			/* stop if we're worse than the current entry */
			if (curpenalty >= penalty[matchnum])
				break;

			/* as lng as this isn't the last entry, bump this one down */
			if (matchnum < matches - 1)
			{
				penalty[matchnum + 1] = penalty[matchnum];
				list[matchnum + 1] = list[matchnum];
			}
			list[matchnum] = driverlist[drvnum];
			penalty[matchnum] = curpenalty;
		}
	}

	/* free our temp memory */
	free(penalty);
}


/*-------------------------------------------------
    penalty_compare - compare two strings for
    closeness and assign a score.
-------------------------------------------------*/

static int penalty_compare(const char *source, const char *target)
{
	int gaps = 1;
	int last = TRUE;

	/* scan the strings */
	for ( ; *source && *target; target++)
	{
		/* do a case insensitive match */
		int match = (tolower(*source) == tolower(*target));

		/* if we matched, advance the source */
		if (match)
			source++;

		/* if the match state changed, count gaps */
		if (match != last)
		{
			last = match;
			if (!match)
				gaps++;
		}
	}

	/* penalty if short string does not completely fit in */
	for ( ; *source; source++)
		gaps++;

	/* if we matched perfectly, gaps == 0 */
	if (gaps == 1 && *source == 0 && *target == 0)
		gaps = 0;

	return gaps;
}


/*-------------------------------------------------
    driver_list_get_count - returns the amount of
    drivers
-------------------------------------------------*/

int driver_list_get_count(const game_driver * const driverlist[])
{
	int count;

	for (count = 0; driverlist[count] != NULL; count++) ;
	return count;
}