summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/emu/emu.mak19
-rw-r--r--src/emu/imagedev/bitbngr.c523
-rw-r--r--src/emu/imagedev/bitbngr.h99
-rw-r--r--src/emu/imagedev/cartslot.c569
-rw-r--r--src/emu/imagedev/cartslot.h180
-rw-r--r--src/emu/imagedev/cassette.c452
-rw-r--r--src/emu/imagedev/cassette.h85
-rw-r--r--src/emu/imagedev/cassimg.c1059
-rw-r--r--src/emu/imagedev/cassimg.h227
-rw-r--r--src/emu/imagedev/chd_cd.c191
-rw-r--r--src/emu/imagedev/chd_cd.h43
-rw-r--r--src/emu/imagedev/flopdrv.c1045
-rw-r--r--src/emu/imagedev/flopdrv.h245
-rw-r--r--src/emu/imagedev/flopimg.c930
-rw-r--r--src/emu/imagedev/flopimg.h204
-rw-r--r--src/emu/imagedev/harddriv.c404
-rw-r--r--src/emu/imagedev/harddriv.h34
-rw-r--r--src/emu/imagedev/multcart.c607
-rw-r--r--src/emu/imagedev/multcart.h100
-rw-r--r--src/emu/imagedev/printer.c116
-rw-r--r--src/emu/imagedev/printer.h48
-rw-r--r--src/emu/imagedev/snapquik.c373
-rw-r--r--src/emu/imagedev/snapquik.h115
-rw-r--r--src/emu/imagedev/wavfile.c291
-rw-r--r--src/emu/imagedev/wavfile.h16
-rw-r--r--src/emu/machine/idectrl.c5
-rw-r--r--src/emu/machine/scsicd.c4
-rw-r--r--src/emu/machine/scsihd.c5
-rw-r--r--src/emu/sound/wave.c8
-rw-r--r--src/emu/uiimage.c425
-rw-r--r--src/emu/uiimage.h4
-rw-r--r--src/emu/uimenu.c19
-rw-r--r--src/mame/drivers/megadriv.c4
-rw-r--r--src/mame/machine/stvcd.c4
-rw-r--r--src/osd/windows/input.c6
35 files changed, 8419 insertions, 40 deletions
diff --git a/src/emu/emu.mak b/src/emu/emu.mak
index d4f2555c481..a9e08fc65bd 100644
--- a/src/emu/emu.mak
+++ b/src/emu/emu.mak
@@ -17,6 +17,7 @@ EMUAUDIO = $(EMUOBJ)/audio
EMUDRIVERS = $(EMUOBJ)/drivers
EMULAYOUT = $(EMUOBJ)/layout
EMUMACHINE = $(EMUOBJ)/machine
+EMUIMAGEDEV = $(EMUOBJ)/imagedev
EMUVIDEO = $(EMUOBJ)/video
OBJDIRS += \
@@ -28,6 +29,7 @@ OBJDIRS += \
$(EMUOBJ)/drivers \
$(EMUOBJ)/machine \
$(EMUOBJ)/layout \
+ $(EMUOBJ)/imagedev \
$(EMUOBJ)/video \
OSDSRC = $(SRC)/osd
@@ -235,7 +237,22 @@ EMUVIDEOOBJS = \
$(EMUVIDEO)/vector.o \
$(EMUVIDEO)/voodoo.o \
-LIBEMUOBJS = $(EMUOBJS) $(EMUSOUNDOBJS) $(EMUAUDIOOBJS) $(EMUDRIVEROBJS) $(EMUMACHINEOBJS) $(EMUVIDEOOBJS)
+EMUIMAGEDEVOBJS = \
+ $(EMUIMAGEDEV)/bitbngr.o \
+ $(EMUIMAGEDEV)/cartslot.o \
+ $(EMUIMAGEDEV)/cassette.o \
+ $(EMUIMAGEDEV)/cassimg.o \
+ $(EMUIMAGEDEV)/chd_cd.o \
+ $(EMUIMAGEDEV)/flopdrv.o \
+ $(EMUIMAGEDEV)/flopimg.o \
+ $(EMUIMAGEDEV)/harddriv.o \
+ $(EMUIMAGEDEV)/multcart.o \
+ $(EMUIMAGEDEV)/printer.o \
+ $(EMUIMAGEDEV)/snapquik.o \
+ $(EMUIMAGEDEV)/wavfile.o \
+
+
+LIBEMUOBJS = $(EMUOBJS) $(EMUSOUNDOBJS) $(EMUAUDIOOBJS) $(EMUDRIVEROBJS) $(EMUMACHINEOBJS) $(EMUIMAGEDEVOBJS) $(EMUVIDEOOBJS)
$(LIBEMU): $(LIBEMUOBJS)
diff --git a/src/emu/imagedev/bitbngr.c b/src/emu/imagedev/bitbngr.c
new file mode 100644
index 00000000000..a0c3651f18f
--- /dev/null
+++ b/src/emu/imagedev/bitbngr.c
@@ -0,0 +1,523 @@
+/*********************************************************************
+
+ bitbngr.c
+
+ TRS style "bitbanger" serial port
+
+*********************************************************************/
+
+#include "emu.h"
+#include "bitbngr.h"
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _bitbanger_token bitbanger_token;
+struct _bitbanger_token
+{
+ emu_timer *bitbanger_output_timer;
+ emu_timer *bitbanger_input_timer;
+ int output_value;
+ int build_count;
+ int build_byte;
+ attotime idle_delay;
+ attotime current_baud;
+ UINT32 input_buffer_size;
+ UINT32 input_buffer_cursor;
+ int mode;
+ int baud;
+ int tune;
+ UINT8 input_buffer[1000];
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static TIMER_CALLBACK(bitbanger_output_timer);
+static TIMER_CALLBACK(bitbanger_input_timer);
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+
+/*-------------------------------------------------
+ get_token - safely gets the bitbanger data
+-------------------------------------------------*/
+
+INLINE bitbanger_token *get_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == BITBANGER);
+ return (bitbanger_token *) downcast<legacy_device_base *>(device)->token();
+}
+
+
+/*-------------------------------------------------
+ get_config - safely gets the bitbanger config
+-------------------------------------------------*/
+
+INLINE const bitbanger_config *get_config(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == BITBANGER);
+ return (const bitbanger_config *) device->baseconfig().static_config();
+}
+
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+
+/*-------------------------------------------------
+ native_output - outputs data to a file
+-------------------------------------------------*/
+
+static void native_output(device_t *bitbanger, UINT8 data)
+{
+ device_image_interface *image = dynamic_cast<device_image_interface *>(bitbanger);
+ if (image->exists())
+ {
+ image->fwrite(&data, 1);
+ }
+}
+
+
+/*-------------------------------------------------
+ native_input - inputs data from a file
+-------------------------------------------------*/
+
+static UINT32 native_input(device_t *bitbanger, void *buffer, UINT32 length)
+{
+ device_image_interface *image = dynamic_cast<device_image_interface *>(bitbanger);
+ if (image->exists())
+ {
+ return image->fread(buffer, length);
+ }
+
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ bitbanger_mode_string
+-------------------------------------------------*/
+const char *bitbanger_mode_string(device_t *device)
+{
+ bitbanger_token *bi = get_token(device);
+ static const char *const modes[] = {"Printer", "Modem"};
+
+ return(modes[bi->mode]);
+}
+
+
+/*-------------------------------------------------
+ bitbanger_inc_mode
+-------------------------------------------------*/
+bool bitbanger_inc_mode(device_t *device, bool test)
+{
+ bitbanger_token *bi = get_token(device);
+ int adjust_mode = (int)bi->mode + 1;
+
+ if( adjust_mode >= BITBANGER_MODE_MAX )
+ return FALSE;
+
+ if( !test)
+ bi->mode = adjust_mode;
+
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ bitbanger_dec_mode
+-------------------------------------------------*/
+bool bitbanger_dec_mode(device_t *device, bool test)
+{
+ bitbanger_token *bi = get_token(device);
+ int adjust_mode = bi->mode - 1;
+
+ if( adjust_mode < 0 )
+ return FALSE;
+
+ if( !test)
+ bi->mode = adjust_mode;
+
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ bitbanger_tune_string
+-------------------------------------------------*/
+const char *bitbanger_tune_string(device_t *device)
+{
+ bitbanger_token *bi = get_token(device);
+ static const char *const tunes[] = {"-2.0%", "-1.75%", "-1.5%", "-1.25%", "-1.0%", "-0.75%", "-0.5", "-0.25%", "\xc2\xb1""0%",
+ "+0.25%", "+0.5%", "+0.75%", "+1.0%", "+1.25%", "+1.5%", "+1.75%", "+2.0%"};
+
+ return(tunes[bi->tune]);
+}
+
+
+/*-------------------------------------------------
+ bitbanger_tune_value
+-------------------------------------------------*/
+static float bitbanger_tune_value(device_t *device)
+{
+ bitbanger_token *bi = get_token(device);
+ static const float tunes[] = {0.97f, 0.9825f, 0.985f, 0.9875f, 0.99f, 0.9925f, 0.995f, 0.9975f, 1.0f,
+ 1.0025f, 1.005f, 1.0075f, 1.01f, 1.0125f, 1.015f, 1.0175f, 1.02f};
+
+ return(tunes[bi->tune]);
+}
+
+
+/*-------------------------------------------------
+ bitbanger_baud_value
+-------------------------------------------------*/
+
+static UINT32 bitbanger_baud_value(device_t *device)
+{
+ bitbanger_token *bi = get_token(device);
+ static const float bauds[] = { 150.0f, 300.0f, 600.0f, 1200.0f, 2400.0f, 4800.0f, 9600.0f,
+ 14400.0f, 28800.0f, 38400.0f, 57600.0f, 115200.0f};
+ float result = bitbanger_tune_value(device) * bauds[bi->baud];
+ return (UINT32)result;
+}
+
+
+/*-------------------------------------------------
+ bitbanger_baud_string
+-------------------------------------------------*/
+
+const char *bitbanger_baud_string(device_t *device)
+{
+ bitbanger_token *bi = get_token(device);
+ static const char *const bauds[] = { "150", "300", "600", "1200", "2400", "4800",
+ "9600", "14400", "28800", "38400", "57600", "115200"};
+
+ return(bauds[bi->baud]);
+}
+
+
+/*-------------------------------------------------
+ bitbanger_inc_baud
+-------------------------------------------------*/
+bool bitbanger_inc_baud(device_t *device, bool test)
+{
+ bitbanger_token *bi = get_token(device);
+ int adjust_baud = (int)bi->baud + 1;
+
+ if( adjust_baud >= BITBANGER_BAUD_MAX )
+ return FALSE;
+
+ if( !test)
+ {
+ bi->baud = adjust_baud;
+ bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device));
+ }
+
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ bitbanger_dec_baud
+-------------------------------------------------*/
+bool bitbanger_dec_baud(device_t *device, bool test)
+{
+ bitbanger_token *bi = get_token(device);
+ int adjust_baud = bi->baud - 1;
+
+ if( adjust_baud < 0 )
+ return FALSE;
+
+ if( !test)
+ {
+ bi->baud = adjust_baud;
+ bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device));
+ }
+
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ bitbanger_inc_tune
+-------------------------------------------------*/
+bool bitbanger_inc_tune(device_t *device, bool test)
+{
+ bitbanger_token *bi = get_token(device);
+ int adjust_tune = (int)bi->tune + 1;
+
+ if( adjust_tune >= BITBANGER_TUNE_MAX )
+ return FALSE;
+
+ if( !test)
+ {
+ bi->tune = adjust_tune;
+ bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device));
+ }
+
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ bitbanger_dec_tune
+-------------------------------------------------*/
+bool bitbanger_dec_tune(device_t *device, bool test)
+{
+ bitbanger_token *bi = get_token(device);
+ int adjust_tune = bi->tune - 1;
+
+ if( adjust_tune < 0 )
+ return FALSE;
+
+ if( !test)
+ {
+ bi->tune = adjust_tune;
+ bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device));
+ }
+
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ bitbanger_bytes_to_bits_81N
+-------------------------------------------------*/
+static void bitbanger_bytes_to_bits_81N(device_t *img)
+{
+ bitbanger_token *bi = get_token(img);
+ UINT8 byte_buffer[100];
+ UINT32 byte_buffer_size, bit_buffer_size;
+ int i, j;
+ static const UINT8 bitmask[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+
+ bit_buffer_size = 0;
+ byte_buffer_size = native_input(img, byte_buffer, sizeof(byte_buffer));
+
+ /* Translate byte buffer into bit buffer using: 1 start bit, 8 data bits, 1 stop bit, no parity */
+
+ for( i=0; i<byte_buffer_size; i++ )
+ {
+ bi->input_buffer[bit_buffer_size++] = 0;
+
+ for( j=0; j<8; j++ )
+ {
+ if( byte_buffer[i] & bitmask[j] )
+ bi->input_buffer[bit_buffer_size++] = 1;
+ else
+ bi->input_buffer[bit_buffer_size++] = 0;
+ }
+
+ bi->input_buffer[bit_buffer_size++] = 1;
+ }
+
+ bi->input_buffer_size = bit_buffer_size;
+ bi->input_buffer_cursor = 0;
+}
+
+
+/*-------------------------------------------------
+ DEVICE_START(bitbanger)
+-------------------------------------------------*/
+
+static DEVICE_START(bitbanger)
+{
+ bitbanger_token *bi;
+ const bitbanger_config *config = get_config(device);
+
+ bi = get_token(device);
+
+ /* output config */
+ bi->build_count = 0;
+ bi->bitbanger_output_timer = timer_alloc(device->machine, bitbanger_output_timer, (void *) device);
+
+ /* input config */
+ bi->bitbanger_input_timer = timer_alloc(device->machine, bitbanger_input_timer, (void *) device );
+ bi->idle_delay = ATTOTIME_IN_SEC(1);
+ bi->input_buffer_size = 0;
+ bi->input_buffer_cursor = 0;
+
+ bi->mode = config->default_mode;
+ bi->baud = config->default_baud;
+ bi->tune = config->default_tune;
+ bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device));
+
+ /* test callback */
+ if(!config->input_callback)
+ fatalerror("Misconfigured bitbanger device: input_callback cannot be NULL\n");
+}
+
+
+/*-------------------------------------------------
+ TIMER_CALLBACK(bitbanger_output_timer)
+-------------------------------------------------*/
+
+static TIMER_CALLBACK(bitbanger_output_timer)
+{
+ device_t *device = (device_t *) ptr;
+ bitbanger_token *bi = get_token(device);
+
+ /* this ia harded coded for 8-1-N */
+ if( bi->output_value )
+ bi->build_byte |= 0x200;
+
+ bi->build_byte >>= 1;
+ bi->build_count--;
+
+ if(bi->build_count == 0)
+ {
+ if( bi->output_value == 1 )
+ native_output( device, bi->build_byte );
+ else
+ logerror("Bitbanger: Output framing error.\n" );
+
+ timer_reset(bi->bitbanger_output_timer, attotime_never);
+ }
+}
+
+
+/*-------------------------------------------------
+ TIMER_CALLBACK(bitbanger_input_timer)
+-------------------------------------------------*/
+
+static TIMER_CALLBACK(bitbanger_input_timer)
+{
+ device_t *device = (device_t *) ptr;
+ bitbanger_token *bi = get_token(device);
+ const bitbanger_config *config = get_config(device);
+
+ if(bi->input_buffer_cursor == bi->input_buffer_size)
+ {
+ /* get more data */
+ bitbanger_bytes_to_bits_81N(device);
+
+ if(bi->input_buffer_size == 0)
+ {
+ /* no more data, wait and try again */
+ bi->idle_delay = attotime_min(attotime_add(bi->idle_delay, ATTOTIME_IN_MSEC(100)), ATTOTIME_IN_SEC(1));
+ timer_adjust_oneshot(bi->bitbanger_input_timer, bi->idle_delay, 0);
+
+
+ if( bi->mode == BITBANGER_MODEM )
+ config->input_callback(machine, 1);
+ else
+ config->input_callback(machine, 0);
+
+ return;
+ }
+ else
+ {
+ bi->idle_delay = bi->current_baud;
+ timer_adjust_periodic(bi->bitbanger_input_timer, bi->idle_delay, 0, bi->idle_delay);
+ }
+ }
+
+ /* send bit to driver */
+ config->input_callback(machine, bi->input_buffer[(bi->input_buffer_cursor)++]);
+}
+
+
+/*-------------------------------------------------
+ bitbanger_output - outputs data to a bitbanger
+ port
+-------------------------------------------------*/
+
+void bitbanger_output(device_t *device, int value)
+{
+ bitbanger_token *bi = get_token(device);
+ attotime one_point_five_baud;
+
+ if( bi->build_count == 0 && bi->output_value == 1 && value == 0 )
+ {
+ /* we found our start bit */
+ /* eight bits of data, plus one of stop */
+ bi->build_count = 9;
+ bi->build_byte = 0;
+
+ one_point_five_baud = attotime_add(bi->current_baud, attotime_div(bi->current_baud,2));
+ timer_adjust_periodic(bi->bitbanger_output_timer, one_point_five_baud, 0, bi->current_baud);
+ }
+
+ //fprintf(stderr,"%s, %d\n", attotime_string(timer_get_time(device->machine),9), value);
+ bi->output_value = value;
+}
+
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_LOAD( bitbanger )
+-------------------------------------------------*/
+
+static DEVICE_IMAGE_LOAD( bitbanger )
+{
+ device_t *device = &image.device();
+ bitbanger_token *bi;
+ bi = get_token(device);
+
+ timer_enable(bi->bitbanger_input_timer, TRUE);
+ timer_adjust_periodic(bi->bitbanger_input_timer, attotime_zero, 0, ATTOTIME_IN_SEC(1));
+
+ /* we don't need to do anything special */
+ return IMAGE_INIT_PASS;
+}
+
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_UNLOAD( bitbanger )
+-------------------------------------------------*/
+
+static DEVICE_IMAGE_UNLOAD( bitbanger )
+{
+ device_t *device = &image.device();
+ bitbanger_token *bi;
+ bi = get_token(device);
+
+ timer_enable(bi->bitbanger_input_timer, FALSE);
+}
+
+
+/*-------------------------------------------------
+ DEVICE_GET_INFO(bitbanger) - device getinfo
+ function
+-------------------------------------------------*/
+
+DEVICE_GET_INFO(bitbanger)
+{
+ switch(state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(bitbanger_token); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+ case DEVINFO_INT_IMAGE_TYPE: info->i = IO_PRINTER; break;
+ case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_CREATABLE: info->i = 1; break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(bitbanger); break;
+ case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(bitbanger); break;
+ case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(bitbanger); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Bitbanger"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Bitbanger"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, "prn"); break;
+ }
+}
+
+DEFINE_LEGACY_IMAGE_DEVICE(BITBANGER, bitbanger);
diff --git a/src/emu/imagedev/bitbngr.h b/src/emu/imagedev/bitbngr.h
new file mode 100644
index 00000000000..85124ca2536
--- /dev/null
+++ b/src/emu/imagedev/bitbngr.h
@@ -0,0 +1,99 @@
+/*********************************************************************
+
+ bitbngr.h
+
+ TRS style "bitbanger" serial port
+
+*********************************************************************/
+
+#ifndef __BITBNGR_H__
+#define __BITBNGR_H__
+
+#include "image.h"
+
+
+enum
+{
+ BITBANGER_PRINTER = 0,
+ BITBANGER_MODEM,
+ BITBANGER_MODE_MAX,
+
+ BITBANGER_150 = 0,
+ BITBANGER_300,
+ BITBANGER_600,
+ BITBANGER_1200,
+ BITBANGER_2400,
+ BITBANGER_4800,
+ BITBANGER_9600,
+ BITBANGER_14400,
+ BITBANGER_28800,
+ BITBANGER_38400,
+ BITBANGER_57600,
+ BITBANGER_115200,
+ BITBANGER_BAUD_MAX,
+
+ BITBANGER_NEG40PERCENT = 0,
+ BITBANGER_NEG35PERCENT,
+ BITBANGER_NEG30PERCENT,
+ BITBANGER_NEG25PERCENT,
+ BITBANGER_NEG20PERCENT,
+ BITBANGER_NEG15PERCENT,
+ BITBANGER_NEG10PERCENT,
+ BITBANGER_NEG5PERCENT,
+ BITBANGER_0PERCENT,
+ BITBANGER_POS5PERCENT,
+ BITBANGER_POS10PERCENT,
+ BITBANGER_POS15PERCENT,
+ BITBANGER_POS20PERCENT,
+ BITBANGER_POS25PERCENT,
+ BITBANGER_POS30PERCENT,
+ BITBANGER_POS35PERCENT,
+ BITBANGER_POS40PERCENT,
+ BITBANGER_TUNE_MAX
+};
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+DECLARE_LEGACY_IMAGE_DEVICE(BITBANGER, bitbanger);
+
+#define MCFG_BITBANGER_ADD(_tag, _intrf) \
+ MCFG_DEVICE_ADD(_tag, BITBANGER, 0) \
+ MCFG_DEVICE_CONFIG(_intrf)
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _bitbanger_config bitbanger_config;
+struct _bitbanger_config
+{
+ /* callback to driver */
+ void (*input_callback)(running_machine *machine, UINT8 bit);
+ int default_mode; /* emulating a printer or modem */
+ int default_baud; /* output bits per second */
+ int default_tune; /* fine tune adjustment to the baud */
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* outputs data to a bitbanger port */
+void bitbanger_output(device_t *device, int value);
+
+/* ui functions */
+const char *bitbanger_mode_string(device_t *device);
+const char *bitbanger_baud_string(device_t *device);
+const char *bitbanger_tune_string(device_t *device);
+bool bitbanger_inc_mode(device_t *device, bool test);
+bool bitbanger_dec_mode(device_t *device, bool test);
+bool bitbanger_inc_tune(device_t *device, bool test);
+bool bitbanger_dec_tune(device_t *device, bool test);
+bool bitbanger_inc_baud(device_t *device, bool test);
+bool bitbanger_dec_baud(device_t *device, bool test);
+
+#endif /* __BITBNGR_H__ */
diff --git a/src/emu/imagedev/cartslot.c b/src/emu/imagedev/cartslot.c
new file mode 100644
index 00000000000..daa7e44e961
--- /dev/null
+++ b/src/emu/imagedev/cartslot.c
@@ -0,0 +1,569 @@
+/**********************************************************************
+
+ cartslot.c
+
+ Cartridge device
+
+**********************************************************************/
+
+#include <ctype.h>
+#include "emu.h"
+#include "cartslot.h"
+#include "multcart.h"
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+enum _process_mode
+{
+ PROCESS_CLEAR,
+ PROCESS_LOAD
+};
+typedef enum _process_mode process_mode;
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+INLINE cartslot_t *get_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == CARTSLOT);
+ return (cartslot_t *) downcast<legacy_device_base *>(device)->token();
+}
+
+
+INLINE const cartslot_config *get_config(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == CARTSLOT);
+ return (const cartslot_config *) downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config();
+}
+
+INLINE const cartslot_config *get_config_dev(const device_config *device)
+{
+ assert(device != NULL);
+ assert(device->type() == CARTSLOT);
+ return (const cartslot_config *)downcast<const legacy_device_config_base *>(device)->inline_config();
+}
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ load_cartridge
+-------------------------------------------------*/
+
+static int load_cartridge(device_image_interface *image, const rom_entry *romrgn, const rom_entry *roment, process_mode mode)
+{
+ const char *region;
+ const char *type;
+ UINT32 flags;
+ offs_t offset, length, read_length, pos = 0, len;
+ UINT8 *ptr;
+ UINT8 clear_val;
+ int datawidth, littleendian, i, j;
+ device_t *cpu;
+
+ region = ROMREGION_GETTAG(romrgn);
+ offset = ROM_GETOFFSET(roment);
+ length = ROM_GETLENGTH(roment);
+ flags = ROM_GETFLAGS(roment);
+ ptr = ((UINT8 *) image->device().machine->region(region)->base()) + offset;
+
+ if (mode == PROCESS_LOAD)
+ {
+ if (image->software_entry() == NULL)
+ {
+ /* must this be full size */
+ if (flags & ROM_FULLSIZE)
+ {
+ if (image->length() != length)
+ return IMAGE_INIT_FAIL;
+ }
+
+ /* read the ROM */
+ pos = read_length = image->fread(ptr, length);
+
+ /* reset the ROM to the initial point. */
+ /* eventually, we could add a flag to allow the ROM to continue instead of restarting whenever a new cart region is present */
+ image->fseek(0, SEEK_SET);
+ }
+ else
+ {
+ /* must this be full size */
+ if (flags & ROM_FULLSIZE)
+ {
+ if (image->get_software_region_length("rom") != length)
+ return IMAGE_INIT_FAIL;
+ }
+
+ /* read the ROM */
+ pos = read_length = image->get_software_region_length("rom");
+ memcpy(ptr, image->get_software_region("rom"), read_length);
+ }
+
+ /* do we need to mirror the ROM? */
+ if (flags & ROM_MIRROR)
+ {
+ while(pos < length)
+ {
+ len = MIN(read_length, length - pos);
+ memcpy(ptr + pos, ptr, len);
+ pos += len;
+ }
+ }
+
+ /* postprocess this region */
+ type = ROMREGION_GETTAG(romrgn);
+ littleendian = ROMREGION_ISLITTLEENDIAN(romrgn);
+ datawidth = ROMREGION_GETWIDTH(romrgn) / 8;
+
+ /* if the region is inverted, do that now */
+ device_memory_interface *memory;
+ cpu = image->device().machine->device(type);
+ if (cpu!=NULL && cpu->interface(memory))
+ {
+ datawidth = device_memory(cpu)->space_config(AS_PROGRAM)->m_databus_width / 8;
+ littleendian = (device_memory(cpu)->space_config()->m_endianness == ENDIANNESS_LITTLE);
+ }
+
+ /* swap the endianness if we need to */
+#ifdef LSB_FIRST
+ if (datawidth > 1 && !littleendian)
+#else
+ if (datawidth > 1 && littleendian)
+#endif
+ {
+ for (i = 0; i < length; i += datawidth)
+ {
+ UINT8 temp[8];
+ memcpy(temp, &ptr[i], datawidth);
+ for (j = datawidth - 1; j >= 0; j--)
+ ptr[i + j] = temp[datawidth - 1 - j];
+ }
+ }
+ }
+
+ /* clear out anything that remains */
+ if (!(flags & ROM_NOCLEAR))
+ {
+ clear_val = (flags & ROM_FILL_FF) ? 0xFF : 0x00;
+ memset(ptr + pos, clear_val, length - pos);
+ }
+ return IMAGE_INIT_PASS;
+}
+
+
+
+/*-------------------------------------------------
+ process_cartridge
+-------------------------------------------------*/
+
+static int process_cartridge(device_image_interface *image, process_mode mode)
+{
+ const rom_source *source;
+ const rom_entry *romrgn, *roment;
+ int result = 0;
+
+ for (source = rom_first_source(*image->device().machine->config); source != NULL; source = rom_next_source(*source))
+ {
+ for (romrgn = rom_first_region(*source); romrgn != NULL; romrgn = rom_next_region(romrgn))
+ {
+ roment = romrgn + 1;
+ while(!ROMENTRY_ISREGIONEND(roment))
+ {
+ if (ROMENTRY_GETTYPE(roment) == ROMENTRYTYPE_CARTRIDGE)
+ {
+ if (strcmp(roment->_hashdata,image->device().tag())==0)
+ {
+ result |= load_cartridge(image, romrgn, roment, mode);
+
+ /* if loading failed in any cart region, stop loading */
+ if (result)
+ return result;
+ }
+ }
+ roment++;
+ }
+ }
+ }
+
+ return IMAGE_INIT_PASS;
+}
+
+
+/*-------------------------------------------------
+ cartslot_get_pcb
+-------------------------------------------------*/
+
+device_t *cartslot_get_pcb(device_t *device)
+{
+ cartslot_t *cart = get_token(device);
+ return cart->pcb_device;
+}
+
+
+/*-------------------------------------------------
+ cartslot_get_socket
+-------------------------------------------------*/
+
+void *cartslot_get_socket(device_t *device, const char *socket_name)
+{
+ cartslot_t *cart = get_token(device);
+ device_image_interface *image = dynamic_cast<device_image_interface *>(device);
+ void *result = NULL;
+
+ if (cart->mc != NULL)
+ {
+ const multicart_socket *socket;
+ for (socket = cart->mc->sockets; socket != NULL; socket = socket->next)
+ {
+ if (!strcmp(socket->id, socket_name))
+ break;
+ }
+ result = socket ? socket->ptr : NULL;
+ }
+ else if (socket_name[0] == '\0')
+ {
+ result = image->ptr();
+ }
+ return result;
+}
+
+/*-------------------------------------------------
+ cartslot_get_resource_length
+-------------------------------------------------*/
+
+int cartslot_get_resource_length(device_t *device, const char *socket_name)
+{
+ cartslot_t *cart = get_token(device);
+ int result = 0;
+
+ if (cart->mc != NULL)
+ {
+ const multicart_socket *socket;
+
+ for (socket = cart->mc->sockets; socket != NULL; socket = socket->next)
+ {
+ if (!strcmp(socket->id, socket_name)) {
+ break;
+ }
+ }
+ if (socket != NULL)
+ result = socket->resource->length;
+ }
+ else
+ result = 0;
+
+ return result;
+}
+
+
+/*-------------------------------------------------
+ DEVICE_START( cartslot )
+-------------------------------------------------*/
+
+static DEVICE_START( cartslot )
+{
+ cartslot_t *cart = get_token(device);
+ const cartslot_config *config = get_config(device);
+
+ /* if this cartridge has a custom DEVICE_START, use it */
+ if (config->device_start != NULL)
+ {
+ (*config->device_start)(device);
+ goto done;
+ }
+
+ /* find the PCB (if there is one) */
+ cart->pcb_device = device->subdevice(TAG_PCB);
+
+done:
+ return;
+}
+
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_LOAD( cartslot )
+-------------------------------------------------*/
+
+static DEVICE_IMAGE_LOAD( cartslot )
+{
+ int result;
+ device_t *device = &image.device();
+ cartslot_t *cart = get_token(device);
+ const cartslot_config *config = get_config(device);
+
+ /* if this cartridge has a custom DEVICE_IMAGE_LOAD, use it */
+ if (config->device_load != NULL)
+ return (*config->device_load)(image);
+
+ /* try opening this as if it were a multicart */
+ multicart_open(image.filename(), device->machine->gamedrv->name, MULTICART_FLAGS_LOAD_RESOURCES, &cart->mc);
+ if (cart->mc == NULL)
+ {
+
+
+ /* otherwise try the normal route */
+ result = process_cartridge(&image, PROCESS_LOAD);
+ if (result != IMAGE_INIT_PASS)
+ return result;
+ }
+
+ return IMAGE_INIT_PASS;
+}
+
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_UNLOAD( cartslot )
+-------------------------------------------------*/
+
+static DEVICE_IMAGE_UNLOAD( cartslot )
+{
+ device_t *device = &image.device();
+ cartslot_t *cart = get_token(device);
+ const cartslot_config *config = get_config(device);
+
+ /* if this cartridge has a custom DEVICE_IMAGE_UNLOAD, use it */
+ if (config->device_unload != NULL)
+ {
+ (*config->device_unload)(image);
+ return;
+ }
+
+ if (cart->mc != NULL)
+ {
+ multicart_close(cart->mc);
+ cart->mc = NULL;
+ }
+
+ process_cartridge(&image, PROCESS_CLEAR);
+}
+
+
+/*-------------------------------------------------
+ identify_pcb
+-------------------------------------------------*/
+
+static const cartslot_pcb_type *identify_pcb(device_image_interface &image)
+{
+ const cartslot_config *config = get_config(&image.device());
+ astring pcb_name;
+ const cartslot_pcb_type *pcb_type = NULL;
+ multicart_t *mc;
+ int i;
+
+ if (image.software_entry() == NULL && image.exists())
+ {
+ /* try opening this as if it were a multicart */
+ multicart_open_error me = multicart_open(image.filename(), image.device().machine->gamedrv->name, MULTICART_FLAGS_DONT_LOAD_RESOURCES, &mc);
+ if (me == MCERR_NONE)
+ {
+ /* this was a multicart - read from it */
+ astring_cpyc(&pcb_name, mc->pcb_type);
+ multicart_close(mc);
+ }
+ else
+ {
+ if (me != MCERR_NOT_MULTICART)
+ fatalerror("multicart error: %s", multicart_error_text(me));
+
+ /* Force fetching of image metadata */
+ image.crc();
+
+ if (image.pcb() != NULL)
+ {
+ /* read from hash file */
+ astring_cpyc(&pcb_name, image.pcb());
+ }
+ }
+
+ /* look for PCB type with matching name */
+ for (i = 0; (i < ARRAY_LENGTH(config->pcb_types)) && (config->pcb_types[i].name != NULL); i++)
+ {
+ if ((config->pcb_types[i].name[0] == '\0') || !strcmp(astring_c(&pcb_name), config->pcb_types[i].name))
+ {
+ pcb_type = &config->pcb_types[i];
+ break;
+ }
+ }
+
+ /* check for unknown PCB type */
+ if ((mc != NULL) && (pcb_type == NULL))
+ fatalerror("Unknown PCB type \"%s\"", astring_c(&pcb_name));
+ }
+ else
+ {
+ /* no device loaded; use the default */
+ pcb_type = (config->pcb_types[0].name != NULL) ? &config->pcb_types[0] : NULL;
+ }
+ return pcb_type;
+}
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_GET_DEVICES(cartslot)
+-------------------------------------------------*/
+static DEVICE_IMAGE_GET_DEVICES(cartslot)
+{
+ const cartslot_pcb_type *pcb_type;
+ device_t *device = &image.device();
+
+ pcb_type = identify_pcb(image);
+ if (pcb_type != NULL)
+ {
+ image_add_device_with_subdevices(device,pcb_type->devtype,TAG_PCB,0);
+ }
+}
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_SOFTLIST_LOAD(cartslot)
+-------------------------------------------------*/
+static DEVICE_IMAGE_SOFTLIST_LOAD(cartslot)
+{
+ load_software_part_region( &image.device(), swlist, swname, start_entry );
+ return TRUE;
+}
+
+/*-------------------------------------------------
+ DEVICE_GET_INFO( cartslot )
+-------------------------------------------------*/
+
+DEVICE_GET_INFO( cartslot )
+{
+ switch(state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(cartslot_t); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(cartslot_config); break;
+ case DEVINFO_INT_IMAGE_TYPE: info->i = IO_CARTSLOT; break;
+ case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 0; break;
+ case DEVINFO_INT_IMAGE_CREATABLE: info->i = 0; break;
+ case DEVINFO_INT_IMAGE_RESET_ON_LOAD: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_MUST_BE_LOADED:
+ if ( device && downcast<const legacy_image_device_config_base *>(device)->inline_config()) {
+ info->i = get_config_dev(device)->must_be_loaded;
+ } else {
+ info->i = 0;
+ }
+ break;
+
+ /* --- the following bits of info are returned as pointers to functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(cartslot); break;
+ case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(cartslot); break;
+ case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(cartslot); break;
+ case DEVINFO_FCT_IMAGE_GET_DEVICES: info->f = (genf *) DEVICE_IMAGE_GET_DEVICES_NAME(cartslot); break;
+ case DEVINFO_FCT_IMAGE_SOFTLIST_LOAD: info->f = (genf *) DEVICE_IMAGE_SOFTLIST_LOAD_NAME(cartslot); break;
+ case DEVINFO_FCT_IMAGE_PARTIAL_HASH:
+ if ( device && downcast<const legacy_image_device_config_base *>(device)->inline_config() && get_config_dev(device)->device_partialhash) {
+ info->f = (genf *) get_config_dev(device)->device_partialhash;
+ } else {
+ info->f = NULL;
+ }
+ break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Cartslot"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Cartslot"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_IMAGE_FILE_EXTENSIONS:
+ if ( device && downcast<const legacy_image_device_config_base *>(device)->inline_config() && get_config_dev(device)->extensions )
+ {
+ strcpy(info->s, get_config_dev(device)->extensions);
+ }
+ else
+ {
+ strcpy(info->s, "bin");
+ }
+ break;
+ case DEVINFO_STR_IMAGE_INTERFACE:
+ if ( device && downcast<const legacy_image_device_config_base *>(device)->inline_config() && get_config_dev(device)->interface )
+ {
+ strcpy(info->s, get_config_dev(device)->interface );
+ }
+ break;
+ }
+}
+
+DEFINE_LEGACY_IMAGE_DEVICE(CARTSLOT, cartslot);
+
+
+//**************************************************************************
+// DEVICE CONFIG CARTSLOT INTERFACE
+//**************************************************************************
+
+//-------------------------------------------------
+// device_config_cart_slot_interface - constructor
+//-------------------------------------------------
+
+device_config_cart_slot_interface::device_config_cart_slot_interface(const machine_config &mconfig, device_config &devconfig)
+ : device_config_interface(mconfig, devconfig)
+{
+}
+
+
+//-------------------------------------------------
+// ~device_config_cart_slot_interface - destructor
+//-------------------------------------------------
+
+device_config_cart_slot_interface::~device_config_cart_slot_interface()
+{
+}
+
+
+
+//**************************************************************************
+// DEVICE CARTSLOT INTERFACE
+//**************************************************************************
+
+//-------------------------------------------------
+// device_cart_slot_interface - constructor
+//-------------------------------------------------
+
+device_cart_slot_interface::device_cart_slot_interface(running_machine &machine, const device_config &config, device_t &device)
+ : device_interface(machine, config, device),
+ m_cart_slot_config(dynamic_cast<const device_config_cart_slot_interface &>(config))
+{
+}
+
+
+//-------------------------------------------------
+// ~device_cart_slot_interface - destructor
+//-------------------------------------------------
+
+device_cart_slot_interface::~device_cart_slot_interface()
+{
+}
+
+//**************************************************************************
+// LEGACY cart_slot DEVICE CONFIGURATION
+//**************************************************************************
+
+//-------------------------------------------------
+// legacy_cart_slot_device_config_base - constructor
+//-------------------------------------------------
+
+legacy_cart_slot_device_config_base::legacy_cart_slot_device_config_base(const machine_config &mconfig, device_type type, const char *tag, const device_config *owner, UINT32 clock, device_get_config_func get_config)
+ : legacy_device_config_base(mconfig, type, tag, owner, clock, get_config),
+ device_config_cart_slot_interface(mconfig, *this)
+{
+}
+
+
+//**************************************************************************
+// LIVE LEGACY cart_slot DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// legacy_cart_slot_device_base - constructor
+//-------------------------------------------------
+
+legacy_cart_slot_device_base::legacy_cart_slot_device_base(running_machine &machine, const device_config &config)
+ : legacy_device_base(machine, config),
+ device_cart_slot_interface(machine, config, *this)
+{
+}
diff --git a/src/emu/imagedev/cartslot.h b/src/emu/imagedev/cartslot.h
new file mode 100644
index 00000000000..d1ae575a70f
--- /dev/null
+++ b/src/emu/imagedev/cartslot.h
@@ -0,0 +1,180 @@
+/***************************************************************************
+
+ Cartrige loading
+
+***************************************************************************/
+
+#ifndef __CARTSLOT_H__
+#define __CARTSLOT_H__
+
+#include "image.h"
+#include "multcart.h"
+
+
+/***************************************************************************
+ MACROS
+***************************************************************************/
+#define TAG_PCB "pcb"
+
+#define ROM_CART_LOAD(tag,offset,length,flags) \
+ { NULL, tag, offset, length, ROMENTRYTYPE_CARTRIDGE | (flags) },
+
+#define ROM_MIRROR 0x01000000
+#define ROM_NOMIRROR 0x00000000
+#define ROM_FULLSIZE 0x02000000
+#define ROM_FILL_FF 0x04000000
+#define ROM_NOCLEAR 0x08000000
+
+DECLARE_LEGACY_IMAGE_DEVICE(CARTSLOT, cartslot);
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _cartslot_t cartslot_t;
+struct _cartslot_t
+{
+ device_t *pcb_device;
+ multicart_t *mc;
+};
+
+
+typedef struct _cartslot_pcb_type cartslot_pcb_type;
+struct _cartslot_pcb_type
+{
+ const char * name;
+ device_type devtype;
+};
+
+
+typedef struct _cartslot_config cartslot_config;
+struct _cartslot_config
+{
+ const char * extensions;
+ const char * interface;
+ int must_be_loaded;
+ device_start_func device_start;
+ device_image_load_func device_load;
+ device_image_unload_func device_unload;
+ device_image_partialhash_func device_partialhash;
+ cartslot_pcb_type pcb_types[16];
+};
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* accesses the PCB associated with this cartslot */
+device_t *cartslot_get_pcb(device_t *device);
+
+/* accesses a particular socket */
+void *cartslot_get_socket(device_t *device, const char *socket_name);
+
+/* accesses a particular socket; gets the length of the associated resource */
+int cartslot_get_resource_length(device_t *device, const char *socket_name);
+
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_CARTSLOT_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, CARTSLOT, 0) \
+
+#define MCFG_CARTSLOT_MODIFY(_tag) \
+ MCFG_DEVICE_MODIFY(_tag) \
+
+#define MCFG_CARTSLOT_EXTENSION_LIST(_extensions) \
+ MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, extensions, _extensions)
+
+#define MCFG_CARTSLOT_NOT_MANDATORY \
+ MCFG_DEVICE_CONFIG_DATA32(cartslot_config, must_be_loaded, FALSE)
+
+#define MCFG_CARTSLOT_MANDATORY \
+ MCFG_DEVICE_CONFIG_DATA32(cartslot_config, must_be_loaded, TRUE)
+
+#define MCFG_CARTSLOT_START(_start) \
+ MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, device_start, DEVICE_START_NAME(_start))
+
+#define MCFG_CARTSLOT_LOAD(_load) \
+ MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, device_load, DEVICE_IMAGE_LOAD_NAME(_load))
+
+#define MCFG_CARTSLOT_UNLOAD(_unload) \
+ MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, device_unload, DEVICE_IMAGE_UNLOAD_NAME(_unload))
+
+#define MCFG_CARTSLOT_PARTIALHASH(_partialhash) \
+ MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, device_partialhash, _partialhash)
+
+#define MCFG_CARTSLOT_PCBTYPE(_index, _pcb_type_name, _pcb_devtype) \
+ MCFG_DEVICE_CONFIG_DATAPTR_ARRAY_MEMBER(cartslot_config, pcb_types, _index, cartslot_pcb_type, name, _pcb_type_name) \
+ MCFG_DEVICE_CONFIG_DATAPTR_ARRAY_MEMBER(cartslot_config, pcb_types, _index, cartslot_pcb_type, devtype, _pcb_devtype)
+
+#define MCFG_CARTSLOT_INTERFACE(_interface) \
+ MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, interface, _interface )
+
+#define DECLARE_LEGACY_CART_SLOT_DEVICE(name, basename) _DECLARE_LEGACY_DEVICE(name, basename, basename##_device_config, basename##_device, legacy_cart_slot_device_config_base, legacy_cart_slot_device_base)
+#define DEFINE_LEGACY_CART_SLOT_DEVICE(name, basename) _DEFINE_LEGACY_DEVICE(name, basename, basename##_device_config, basename##_device, legacy_cart_slot_device_config_base, legacy_cart_slot_device_base)
+
+// ======================> device_config_cart_slot_interface
+
+// class representing interface-specific configuration cart_slot
+class device_config_cart_slot_interface : public device_config_interface
+{
+public:
+ // construction/destruction
+ device_config_cart_slot_interface(const machine_config &mconfig, device_config &device);
+ virtual ~device_config_cart_slot_interface();
+};
+
+
+
+// ======================> device_cart_slot_interface
+
+// class representing interface-specific live cart_slot
+class device_cart_slot_interface : public device_interface
+{
+public:
+ // construction/destruction
+ device_cart_slot_interface(running_machine &machine, const device_config &config, device_t &device);
+ virtual ~device_cart_slot_interface();
+
+ // configuration access
+ const device_config_cart_slot_interface &cart_slot_config() const { return m_cart_slot_config; }
+protected:
+
+ // configuration
+ const device_config_cart_slot_interface &m_cart_slot_config; // reference to our device_config_execute_interface
+};
+// ======================> legacy_cart_slot_device_config
+
+// legacy_cart_slot_device_config is a device_config with a cart_slot interface
+class legacy_cart_slot_device_config_base : public legacy_device_config_base,
+ public device_config_cart_slot_interface
+{
+protected:
+ // construction/destruction
+ legacy_cart_slot_device_config_base(const machine_config &mconfig, device_type type, const char *tag, const device_config *owner, UINT32 clock, device_get_config_func get_config);
+public:
+ INT64 get_config_int(UINT32 state) const { return get_legacy_config_int(state); }
+ genf *get_config_fct(UINT32 state) const { return get_legacy_config_fct(state); }
+ void *get_config_ptr(UINT32 state) const { return get_legacy_config_ptr(state); }
+};
+
+
+
+// ======================> legacy_cart_slot_device
+
+// legacy_cart_slot_device is a legacy_device_base with a cart_slot interface
+class legacy_cart_slot_device_base : public legacy_device_base,
+ public device_cart_slot_interface
+{
+protected:
+ // construction/destruction
+ legacy_cart_slot_device_base(running_machine &machine, const device_config &config);
+
+ // device_cart_slot_interface overrides
+};
+#endif /* __cart_slot_H__ */
diff --git a/src/emu/imagedev/cassette.c b/src/emu/imagedev/cassette.c
new file mode 100644
index 00000000000..60767fcf6bd
--- /dev/null
+++ b/src/emu/imagedev/cassette.c
@@ -0,0 +1,452 @@
+/*********************************************************************
+
+ cassette.c
+
+ MESS interface to the cassette image abstraction code
+
+*********************************************************************/
+
+#include "emu.h"
+#include "imageutl.h"
+#include "cassette.h"
+#include "cassimg.h"
+#include "ui.h"
+
+
+#define CASSETTE_TAG "cassette"
+#define ANIMATION_FPS 1
+#define ANIMATION_FRAMES 4
+
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+typedef struct _dev_cassette_t dev_cassette_t;
+struct _dev_cassette_t
+{
+ const cassette_config *config;
+ cassette_image *cassette;
+ cassette_state state;
+ double position;
+ double position_time;
+ INT32 value;
+};
+
+
+/* Default cassette_config for drivers only wav files */
+const cassette_config default_cassette_config =
+{
+ cassette_default_formats,
+ NULL,
+ CASSETTE_PLAY,
+ NULL
+};
+
+
+INLINE dev_cassette_t *get_safe_token(device_t *device)
+{
+ assert( device != NULL );
+ assert( device->type() == CASSETTE );
+ return (dev_cassette_t *) downcast<legacy_device_base *>(device)->token();
+}
+
+
+/*********************************************************************
+ cassette IO
+*********************************************************************/
+
+INLINE int cassette_is_motor_on(device_t *device)
+{
+ dev_cassette_t *cassette = get_safe_token( device );
+
+ if ((cassette->state & CASSETTE_MASK_UISTATE) == CASSETTE_STOPPED)
+ return FALSE;
+ if ((cassette->state & CASSETTE_MASK_MOTOR) != CASSETTE_MOTOR_ENABLED)
+ return FALSE;
+ return TRUE;
+}
+
+
+
+static void cassette_update(device_t *device)
+{
+ dev_cassette_t *cassette = get_safe_token( device );
+ double cur_time = attotime_to_double(timer_get_time(device->machine));
+
+ if (cassette_is_motor_on(device))
+ {
+ double new_position = cassette->position + (cur_time - cassette->position_time);
+
+ switch(cassette->state & CASSETTE_MASK_UISTATE) {
+ case CASSETTE_RECORD:
+ cassette_put_sample(cassette->cassette, 0, cassette->position, new_position - cassette->position, cassette->value);
+ break;
+
+ case CASSETTE_PLAY:
+ if ( cassette->cassette )
+ {
+ cassette_get_sample(cassette->cassette, 0, new_position, 0.0, &cassette->value);
+ /* See if reached end of tape */
+ double length = cassette_get_length(device);
+ if (new_position > length)
+ {
+ cassette->state = (cassette_state)(( cassette->state & ~CASSETTE_MASK_UISTATE ) | CASSETTE_STOPPED);
+ new_position = length;
+ }
+ }
+ break;
+ }
+ cassette->position = new_position;
+ }
+ cassette->position_time = cur_time;
+}
+
+
+
+cassette_state cassette_get_state(device_t *device)
+{
+ dev_cassette_t *cassette = get_safe_token( device );
+ return cassette->state;
+}
+
+
+
+void cassette_change_state(device_t *device, cassette_state state, cassette_state mask)
+{
+ dev_cassette_t *cassette = get_safe_token( device );
+ cassette_state new_state;
+
+ new_state = cassette->state;
+ new_state = (cassette_state)(new_state & ~mask);
+ new_state = (cassette_state)(new_state | (state & mask));
+
+ if (new_state != cassette->state)
+ {
+ cassette_update(device);
+ cassette->state = new_state;
+ }
+}
+
+
+
+void cassette_set_state(device_t *device, cassette_state state)
+{
+ cassette_change_state(device, state, (cassette_state)(~0));
+}
+
+
+
+double cassette_input(device_t *device)
+{
+ dev_cassette_t *cassette = get_safe_token( device );
+ INT32 sample;
+ double double_value;
+
+ cassette_update(device);
+ sample = cassette->value;
+ double_value = sample / ((double) 0x7FFFFFFF);
+
+ LOG(("cassette_input(): time_index=%g value=%g\n", cassette->position, double_value));
+
+ return double_value;
+}
+
+
+
+void cassette_output(device_t *device, double value)
+{
+ dev_cassette_t *cassette = get_safe_token( device );
+
+ if (((cassette->state & CASSETTE_MASK_UISTATE) == CASSETTE_RECORD) && (cassette->value != value))
+ {
+ cassette_update(device);
+
+ value = MIN(value, 1.0);
+ value = MAX(value, -1.0);
+
+ cassette->value = (INT32) (value * 0x7FFFFFFF);
+ }
+}
+
+
+
+cassette_image *cassette_get_image(device_t *device)
+{
+ dev_cassette_t *cassette = get_safe_token( device );
+
+ return cassette->cassette;
+}
+
+
+
+double cassette_get_position(device_t *device)
+{
+ dev_cassette_t *cassette = get_safe_token( device );
+ double position;
+
+ position = cassette->position;
+
+ if (cassette_is_motor_on(device))
+ position += attotime_to_double(timer_get_time(device->machine)) - cassette->position_time;
+ return position;
+}
+
+
+
+double cassette_get_length(device_t *device)
+{
+ dev_cassette_t *cassette = get_safe_token( device );
+ struct CassetteInfo info;
+
+ cassette_get_info(cassette->cassette, &info);
+ return ((double) info.sample_count) / info.sample_frequency;
+}
+
+
+
+void cassette_seek(device_t *device, double time, int origin)
+{
+ dev_cassette_t *cassette = get_safe_token( device );
+
+ double length;
+
+ cassette_update(device);
+
+ length = cassette_get_length(device);
+
+ switch(origin) {
+ case SEEK_SET:
+ break;
+
+ case SEEK_END:
+ time += length;
+ break;
+
+ case SEEK_CUR:
+ time += cassette_get_position(device);
+ break;
+ }
+
+ /* clip position into legal bounds */
+ if (time < 0)
+ time = 0;
+ else
+ if (time > length)
+ time = length;
+
+ cassette->position = time;
+}
+
+
+
+/*********************************************************************
+ cassette device init/load/unload/specify
+*********************************************************************/
+
+static DEVICE_START( cassette )
+{
+ dev_cassette_t *cassette = get_safe_token( device );
+
+ /* set to default state */
+ cassette->config = (const cassette_config*)device->baseconfig().static_config();
+ cassette->cassette = NULL;
+ cassette->state = cassette->config->default_state;
+}
+
+
+
+static DEVICE_IMAGE_LOAD( cassette )
+{
+ device_t *device = &image.device();
+ dev_cassette_t *cassette = get_safe_token( device );
+ casserr_t err;
+ int cassette_flags;
+ const struct CassetteFormat * const *formats;
+ const struct CassetteOptions *create_opts;
+ const char *extension;
+ int is_writable;
+
+ /* figure out the cassette format */
+ formats = cassette->config->formats;
+
+ if (image.has_been_created())
+ {
+ /* creating an image */
+ create_opts = cassette->config->create_opts;
+ err = cassette_create(device->machine, (void *) &image, &image_ioprocs, &wavfile_format, create_opts, CASSETTE_FLAG_READWRITE|CASSETTE_FLAG_SAVEONEXIT, &cassette->cassette);
+ if (err)
+ goto error;
+ }
+ else
+ {
+ /* opening an image */
+ do
+ {
+ is_writable = image.is_writable();
+ cassette_flags = is_writable ? (CASSETTE_FLAG_READWRITE|CASSETTE_FLAG_SAVEONEXIT) : CASSETTE_FLAG_READONLY;
+ extension = image.filetype();
+ err = cassette_open_choices(device->machine,(void *) &image, &image_ioprocs, extension, formats, cassette_flags, &cassette->cassette);
+
+ /* this is kind of a hack */
+ if (err && is_writable)
+ image.make_readonly();
+ }
+ while(err && is_writable);
+
+ if (err)
+ goto error;
+ }
+
+ /* set to default state, but only change the UI state */
+ cassette_change_state(device, cassette->config->default_state, CASSETTE_MASK_UISTATE);
+
+ /* reset the position */
+ cassette->position = 0.0;
+ cassette->position_time = attotime_to_double(timer_get_time(device->machine));
+
+ return IMAGE_INIT_PASS;
+
+error:
+ return IMAGE_INIT_FAIL;
+}
+
+
+
+static DEVICE_IMAGE_UNLOAD( cassette )
+{
+ device_t *device = &image.device();
+ dev_cassette_t *cassette = get_safe_token( device );
+
+ /* if we are recording, write the value to the image */
+ if ((cassette->state & CASSETTE_MASK_UISTATE) == CASSETTE_RECORD)
+ cassette_update(device);
+
+ /* close out the cassette */
+ cassette_close(cassette->cassette);
+ cassette->cassette = NULL;
+
+ /* set to default state, but only change the UI state */
+ cassette_change_state(device, CASSETTE_STOPPED, CASSETTE_MASK_UISTATE);
+}
+
+
+
+/*
+ display a small tape icon, with the current position in the tape image
+*/
+static DEVICE_IMAGE_DISPLAY(cassette)
+{
+ device_t *device = &image.device();
+ char buf[65];
+ float x, y;
+ int n;
+ double position, length;
+ cassette_state uistate;
+ device_t *dev;
+ static const UINT8 shapes[8] = { 0x2d, 0x5c, 0x7c, 0x2f, 0x2d, 0x20, 0x20, 0x20 };
+
+ /* abort if we should not be showing the image */
+ if (!image.exists())
+ return;
+ if (!cassette_is_motor_on(device))
+ return;
+
+ /* figure out where we are in the cassette */
+ position = cassette_get_position(device);
+ length = cassette_get_length(device);
+ uistate = (cassette_state)(cassette_get_state(device) & CASSETTE_MASK_UISTATE);
+
+ /* choose a location on the screen */
+ x = 0.2f;
+ y = 0.5f;
+
+ dev = device->machine->m_devicelist.first(CASSETTE );
+
+ while ( dev && strcmp( dev->tag(), device->tag() ) )
+ {
+ y += 1;
+ dev = dev->typenext();
+ }
+
+ y *= ui_get_line_height(*device->machine) + 2.0f * UI_BOX_TB_BORDER;
+ /* choose which frame of the animation we are at */
+ n = ((int) position / ANIMATION_FPS) % ANIMATION_FRAMES;
+ /* Since you can have anything in a BDF file, we will use crude ascii characters instead */
+ snprintf(buf, ARRAY_LENGTH(buf), "%c%c %c %02d:%02d (%04d) [%02d:%02d (%04d)]",
+#if 0
+ /* THE ANIMATION HASN'T WORKED SINCE 0.114 - LEFT HERE FOR REFERENCE */
+ /* NEVER SEEN THE PLAY / RECORD ICONS */
+ /* character pairs 2-3, 4-5, 6-7, 8-9 form little tape cassette images */
+ n * 2 + 2, /* cassette icon left */
+ n * 2 + 3, /* cassette icon right */
+ (uistate == CASSETTE_PLAY) ? 16 : 14, /* play or record icon */
+#else
+ shapes[n], /* cassette icon left */
+ shapes[n|4], /* cassette icon right */
+ (uistate == CASSETTE_PLAY) ? 0x50 : 0x52, /* play (P) or record (R) */
+#endif
+ ((int) position / 60),
+ ((int) position % 60),
+ (int) position,
+ ((int) length / 60),
+ ((int) length % 60),
+ (int) length);
+
+ /* draw the cassette */
+ ui_draw_text_box(&device->machine->render().ui_container(), buf, JUSTIFY_LEFT, x, y, UI_BACKGROUND_COLOR);
+}
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_SOFTLIST_LOAD(cassette)
+-------------------------------------------------*/
+static DEVICE_IMAGE_SOFTLIST_LOAD(cassette)
+{
+ return image.load_software(swlist, swname, start_entry);
+}
+
+DEVICE_GET_INFO(cassette)
+{
+ switch( state )
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(dev_cassette_t); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+ case DEVINFO_INT_IMAGE_TYPE: info->i = IO_CASSETTE; break;
+ case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_CREATABLE: info->i = 1; break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(cassette); break;
+ case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(cassette); break;
+ case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(cassette); break;
+ case DEVINFO_FCT_IMAGE_DISPLAY: info->f = (genf *) DEVICE_IMAGE_DISPLAY_NAME(cassette); break;
+ case DEVINFO_FCT_IMAGE_SOFTLIST_LOAD: info->f = (genf *) DEVICE_IMAGE_SOFTLIST_LOAD_NAME(cassette); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Cassette"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Cassette"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_IMAGE_FILE_EXTENSIONS:
+ if ( device && device->static_config() )
+ {
+ const struct CassetteFormat * const *formats = ((cassette_config *)device->static_config())->formats;
+ int i;
+
+ /* set up a temporary string */
+ info->s[0] = '\0';
+
+ for ( i = 0; formats[i]; i++ )
+ image_specify_extension( info->s, 256, formats[i]->extensions );
+ }
+ break;
+ case DEVINFO_STR_IMAGE_INTERFACE:
+ if ( device && device->static_config() && ((cassette_config *)device->static_config())->interface)
+ {
+ strcpy(info->s, ((cassette_config *)device->static_config())->interface );
+ }
+ break;
+ }
+}
+
+DEFINE_LEGACY_IMAGE_DEVICE(CASSETTE, cassette);
diff --git a/src/emu/imagedev/cassette.h b/src/emu/imagedev/cassette.h
new file mode 100644
index 00000000000..50f5ba59326
--- /dev/null
+++ b/src/emu/imagedev/cassette.h
@@ -0,0 +1,85 @@
+/*********************************************************************
+
+ cassette.h
+
+ MESS interface to the cassette image abstraction code
+
+*********************************************************************/
+
+#ifndef CASSETTE_H
+#define CASSETTE_H
+
+#include "image.h"
+#include "cassimg.h"
+
+
+enum _cassette_state
+{
+ /* this part of the state is controlled by the UI */
+ CASSETTE_STOPPED = 0,
+ CASSETTE_PLAY = 1,
+ CASSETTE_RECORD = 2,
+
+ /* this part of the state is controlled by drivers */
+ CASSETTE_MOTOR_ENABLED = 0,
+ CASSETTE_MOTOR_DISABLED = 4,
+ CASSETTE_SPEAKER_ENABLED = 0,
+ CASSETTE_SPEAKER_MUTED = 8,
+
+ /* masks */
+ CASSETTE_MASK_UISTATE = 3,
+ CASSETTE_MASK_MOTOR = 4,
+ CASSETTE_MASK_SPEAKER = 8,
+ CASSETTE_MASK_DRVSTATE = 12
+};
+
+typedef enum _cassette_state cassette_state;
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct cassette_config_t cassette_config;
+struct cassette_config_t
+{
+ const struct CassetteFormat* const *formats;
+ const struct CassetteOptions *create_opts;
+ const cassette_state default_state;
+ const char * interface;
+};
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+cassette_state cassette_get_state(device_t *cassette);
+void cassette_set_state(device_t *cassette, cassette_state state);
+void cassette_change_state(device_t *cassette, cassette_state state, cassette_state mask);
+
+double cassette_input(device_t *cassette);
+void cassette_output(device_t *cassette, double value);
+
+cassette_image *cassette_get_image(device_t *cassette);
+double cassette_get_position(device_t *cassette);
+double cassette_get_length(device_t *cassette);
+void cassette_seek(device_t *cassette, double time, int origin);
+
+DECLARE_LEGACY_IMAGE_DEVICE(CASSETTE, cassette);
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_CASSETTE_ADD(_tag, _config) \
+ MCFG_DEVICE_ADD(_tag, CASSETTE, 0) \
+ MCFG_DEVICE_CONFIG(_config)
+
+#define MCFG_CASSETTE_MODIFY(_tag, _config) \
+ MCFG_DEVICE_MODIFY(_tag) \
+ MCFG_DEVICE_CONFIG(_config)
+
+extern const cassette_config default_cassette_config;
+
+#endif /* CASSETTE_H */
diff --git a/src/emu/imagedev/cassimg.c b/src/emu/imagedev/cassimg.c
new file mode 100644
index 00000000000..f2e3c9b2eb7
--- /dev/null
+++ b/src/emu/imagedev/cassimg.c
@@ -0,0 +1,1059 @@
+/*********************************************************************
+
+ cassimg.c
+
+ Cassette tape image abstraction code
+
+*********************************************************************/
+
+
+#include <string.h>
+
+#include "emu.h"
+#include "imageutl.h"
+#include "pool.h"
+#include "cassimg.h"
+
+
+/* debugging parameters */
+#define LOG_PUT_SAMPLES 0
+#define DUMP_CASSETTES 0
+
+#define SAMPLES_PER_BLOCK 0x40000
+#define CASSETTE_FLAG_DIRTY 0x10000
+
+
+CASSETTE_FORMATLIST_START(cassette_default_formats)
+CASSETTE_FORMATLIST_END
+
+
+
+/*********************************************************************
+ helper code
+*********************************************************************/
+
+static double map_double(double d, UINT64 low, UINT64 high, UINT64 value)
+{
+#if defined(_MSC_VER) && (_MSC_VER <= 1200)
+ /* casting unsigned __int64 to double is not supported on VC6 or before */
+ return d * (INT64)(value - low) / (INT64)(high - low);
+#else
+ return d * (value - low) / (high - low);
+#endif
+}
+
+
+
+static size_t waveform_bytes_per_sample(int waveform_flags)
+{
+ return (size_t) (1 << ((waveform_flags & 0x06) / 2));
+}
+
+
+
+/*********************************************************************
+ extrapolation and interpolation
+*********************************************************************/
+
+static INT32 extrapolate8(INT8 value)
+{
+ return ((INT32) value) << 24;
+}
+
+static INT32 extrapolate16(INT16 value)
+{
+ return ((INT32) value) << 16;
+}
+
+static INT8 interpolate8(INT32 value)
+{
+ return (INT8) (value >> 24);
+}
+
+static INT16 interpolate16(INT32 value)
+{
+ return (INT16) (value >> 16);
+}
+
+
+
+/*********************************************************************
+ initialization and termination
+*********************************************************************/
+
+static cassette_image *cassette_init(running_machine *machine,const struct CassetteFormat *format, void *file, const struct io_procs *procs, int flags)
+{
+ cassette_image *cassette;
+
+ cassette = (cassette_image *)malloc(sizeof(struct _cassette_image));
+ if (!cassette)
+ return NULL;
+
+ memset(cassette, 0, sizeof(*cassette));
+ cassette->format = format;
+ cassette->io.file = file;
+ cassette->io.procs = procs;
+ cassette->flags = flags;
+ cassette->pool = pool_alloc_lib(NULL);
+ cassette->machine = machine;
+ return cassette;
+}
+
+
+
+static void cassette_finishinit(casserr_t err, cassette_image *cassette, cassette_image **outcassette)
+{
+ if (cassette && (err || !outcassette))
+ {
+ cassette_close(cassette);
+ cassette = NULL;
+ }
+ if (outcassette)
+ *outcassette = cassette;
+}
+
+
+
+static int good_format(const struct CassetteFormat *format, const char *extension, int flags)
+{
+ if (extension && !image_find_extension(format->extensions, extension))
+ return FALSE;
+ if (((flags & CASSETTE_FLAG_READONLY) == 0) && !format->save)
+ return FALSE;
+ return TRUE;
+}
+
+
+
+casserr_t cassette_open_choices(running_machine *machine,void *file, const struct io_procs *procs, const char *extension,
+ const struct CassetteFormat *const *formats, int flags, cassette_image **outcassette)
+{
+ casserr_t err;
+ cassette_image *cassette;
+ const struct CassetteFormat *format;
+ struct CassetteOptions opts = {0, };
+ int i;
+
+ /* if not specified, use the dummy arguments */
+ if (!formats)
+ formats = cassette_default_formats;
+
+ /* create the cassette object */
+ cassette = cassette_init(machine, NULL, file, procs, flags);
+ if (!cassette)
+ {
+ err = CASSETTE_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* identify the image */
+ format = NULL;
+ for (i = 0; !format && formats[i]; i++)
+ {
+ if (good_format(formats[i], extension, flags))
+ {
+ format = formats[i];
+ memset(&opts, 0, sizeof(opts));
+ err = format->identify(cassette, &opts);
+ if (err == CASSETTE_ERROR_INVALIDIMAGE)
+ format = NULL;
+ else if (err)
+ goto done;
+ }
+ }
+
+ /* have we found a proper format */
+ if (!format)
+ {
+ err = CASSETTE_ERROR_INVALIDIMAGE;
+ goto done;
+ }
+ cassette->format = format;
+
+ /* read the options */
+ cassette->channels = opts.channels;
+ cassette->sample_frequency = opts.sample_frequency;
+
+ /* load the image */
+ err = format->load(cassette);
+ if (err)
+ goto done;
+
+ /* success */
+ cassette->flags &= ~CASSETTE_FLAG_DIRTY;
+ err = CASSETTE_ERROR_SUCCESS;
+
+done:
+ cassette_finishinit(err, cassette, outcassette);
+ return err;
+}
+
+
+
+casserr_t cassette_open(running_machine *machine, void *file, const struct io_procs *procs,
+ const struct CassetteFormat *format, int flags, cassette_image **outcassette)
+{
+ const struct CassetteFormat *formats[2];
+ formats[0] = format;
+ formats[1] = NULL;
+ return cassette_open_choices(machine, file, procs, NULL, formats, flags, outcassette);
+}
+
+
+
+casserr_t cassette_create(running_machine *machine, void *file, const struct io_procs *procs, const struct CassetteFormat *format,
+ const struct CassetteOptions *opts, int flags, cassette_image **outcassette)
+{
+ casserr_t err;
+ cassette_image *cassette;
+ static const struct CassetteOptions default_options = { 1, 16, 44100 };
+
+ /* cannot create to a read only image */
+ if (flags & CASSETTE_FLAG_READONLY)
+ return CASSETTE_ERROR_INVALIDIMAGE;
+
+ /* is this a good format? */
+ if (!good_format(format, NULL, flags))
+ return CASSETTE_ERROR_INVALIDIMAGE;
+
+ /* normalize arguments */
+ if (!opts)
+ opts = &default_options;
+
+ /* create the cassette object */
+ cassette = cassette_init(machine, format, file, procs, flags);
+ if (!cassette)
+ {
+ err = CASSETTE_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* read the options */
+ cassette->channels = opts->channels;
+ cassette->sample_frequency = opts->sample_frequency;
+
+ err = CASSETTE_ERROR_SUCCESS;
+
+done:
+ cassette_finishinit(err, cassette, outcassette);
+ return err;
+}
+
+
+
+static casserr_t cassette_perform_save(cassette_image *cassette)
+{
+ struct CassetteInfo info;
+ cassette_get_info(cassette, &info);
+ return cassette->format->save(cassette, &info);
+}
+
+
+
+casserr_t cassette_save(cassette_image *cassette)
+{
+ casserr_t err;
+
+ if (!cassette->format || !cassette->format->save)
+ return CASSETTE_ERROR_UNSUPPORTED;
+
+ err = cassette_perform_save(cassette);
+ if (err)
+ return err;
+
+ cassette->flags &= ~CASSETTE_FLAG_DIRTY;
+ return CASSETTE_ERROR_SUCCESS;
+}
+
+
+
+void cassette_get_info(cassette_image *cassette, struct CassetteInfo *info)
+{
+ memset(info, 0, sizeof(*info));
+ info->channels = cassette->channels;
+ info->sample_count = cassette->sample_count;
+ info->sample_frequency = cassette->sample_frequency;
+ info->bits_per_sample = (int) waveform_bytes_per_sample(cassette->flags) * 8;
+}
+
+
+
+void cassette_close(cassette_image *cassette)
+{
+ if (cassette)
+ {
+ if ((cassette->flags & CASSETTE_FLAG_DIRTY) && (cassette->flags & CASSETTE_FLAG_SAVEONEXIT))
+ cassette_save(cassette);
+ pool_free_lib(cassette->pool);
+ free(cassette);
+ }
+}
+
+
+
+void cassette_change(cassette_image *cassette, void *file, const struct io_procs *procs, const struct CassetteFormat *format, int flags)
+{
+ if ((flags & CASSETTE_FLAG_READONLY) == 0)
+ flags |= CASSETTE_FLAG_DIRTY;
+ cassette->io.file = file;
+ cassette->io.procs = procs;
+ cassette->format = format;
+ cassette->flags = flags;
+}
+
+
+
+/*********************************************************************
+ calls for accessing the raw cassette image
+*********************************************************************/
+
+void cassette_image_read(cassette_image *cassette, void *buffer, UINT64 offset, size_t length)
+{
+ io_generic_read(&cassette->io, buffer, offset, length);
+}
+
+
+
+void cassette_image_write(cassette_image *cassette, const void *buffer, UINT64 offset, size_t length)
+{
+ io_generic_write(&cassette->io, buffer, offset, length);
+}
+
+
+
+UINT64 cassette_image_size(cassette_image *cassette)
+{
+ return io_generic_size(&cassette->io);
+}
+
+
+
+/*********************************************************************
+ waveform accesses
+*********************************************************************/
+
+struct manipulation_ranges
+{
+ int channel_first;
+ int channel_last;
+ size_t sample_first;
+ size_t sample_last;
+};
+
+
+
+static size_t my_round(double d)
+{
+ size_t result;
+ d += 0.5;
+ result = (size_t) d;
+ return result;
+}
+
+
+
+static casserr_t compute_manipulation_ranges(cassette_image *cassette, int channel,
+ double time_index, double sample_period, struct manipulation_ranges *ranges)
+{
+ if (channel < 0)
+ {
+ ranges->channel_first = 0;
+ ranges->channel_last = cassette->channels - 1;
+ }
+ else
+ {
+ ranges->channel_first = channel;
+ ranges->channel_last = channel;
+ }
+
+ ranges->sample_first = my_round(time_index * cassette->sample_frequency);
+ ranges->sample_last = my_round((time_index + sample_period) * cassette->sample_frequency);
+
+ if (ranges->sample_last > ranges->sample_first)
+ ranges->sample_last--;
+
+ return CASSETTE_ERROR_SUCCESS;
+}
+
+
+
+static casserr_t lookup_sample(cassette_image *cassette, int channel, size_t sample, int allocate, INT32 **ptr)
+{
+ size_t sample_block;
+ size_t sample_index;
+ size_t sample_size;
+ size_t new_block_count;
+ size_t new_block_sample_count;
+ INT32 *new_block;
+ struct sample_block *new_blocks;
+ struct sample_block *block;
+
+ *ptr = NULL;
+ sample_block = (sample / SAMPLES_PER_BLOCK) * cassette->channels + channel;
+ sample_index = sample % SAMPLES_PER_BLOCK;
+ sample_size = sizeof(block->block[0]);
+
+ /* is this block beyond the edge of our waveform? */
+ if (sample_block >= cassette->block_count)
+ {
+ if (!allocate)
+ return CASSETTE_ERROR_SUCCESS;
+
+ /* allocate new blocks */
+ new_block_count = sample_block + 1;
+ new_blocks = (struct sample_block *)pool_realloc_lib(cassette->pool, cassette->blocks, new_block_count * sizeof(cassette->blocks[0]));
+ if (!new_blocks)
+ return CASSETTE_ERROR_OUTOFMEMORY;
+
+ cassette->blocks = new_blocks;
+ memset(&cassette->blocks[cassette->block_count], 0, (new_block_count - cassette->block_count) * sizeof(cassette->blocks[0]));
+ cassette->block_count = new_block_count;
+ }
+
+ block = &cassette->blocks[sample_block];
+
+ /* is this sample access off the current block? */
+ if (sample_index >= block->sample_count)
+ {
+ if (!allocate)
+ return CASSETTE_ERROR_SUCCESS;
+
+ new_block_sample_count = SAMPLES_PER_BLOCK;
+
+ new_block = (INT32*)pool_realloc_lib(cassette->pool, block->block, new_block_sample_count * sample_size);
+ if (!new_block)
+ return CASSETTE_ERROR_OUTOFMEMORY;
+
+ block->block = new_block;
+ memset(&block->block[block->sample_count], 0, (new_block_sample_count - block->sample_count) * sample_size);
+ block->sample_count = new_block_sample_count;
+ }
+
+ *ptr = &block->block[sample_index];
+ return CASSETTE_ERROR_SUCCESS;
+}
+
+
+
+/*********************************************************************
+ waveform accesses
+*********************************************************************/
+
+casserr_t cassette_get_samples(cassette_image *cassette, int channel,
+ double time_index, double sample_period, size_t sample_count, size_t sample_bytes,
+ void *samples, int waveform_flags)
+{
+ casserr_t err;
+ struct manipulation_ranges ranges;
+ size_t sample_index;
+ size_t cassette_sample_index;
+ UINT8 *dest_ptr;
+ const INT32 *source_ptr;
+ double d;
+ INT16 word;
+ INT32 dword;
+ INT64 sum;
+
+ assert(cassette);
+
+ err = compute_manipulation_ranges(cassette, channel, time_index, sample_period, &ranges);
+ if (err)
+ return err;
+
+ for (sample_index = 0; sample_index < sample_count; sample_index++)
+ {
+ sum = 0;
+
+ for (channel = ranges.channel_first; channel <= ranges.channel_last; channel++)
+ {
+ /* find the sample that we are putting */
+ d = map_double(ranges.sample_last + 1 - ranges.sample_first, 0, sample_count, sample_index) + ranges.sample_first;
+ cassette_sample_index = (size_t) d;
+ err = lookup_sample(cassette, channel, cassette_sample_index, TRUE, (INT32 **) &source_ptr);
+ if (err)
+ return err;
+
+ sum += *source_ptr;
+ }
+
+ /* average out the samples */
+ sum /= (ranges.channel_last + 1 - ranges.channel_first);
+
+ /* and write out the result */
+ dest_ptr = (UINT8*)samples;
+ dest_ptr += waveform_bytes_per_sample(waveform_flags) * sample_index;
+ switch(waveform_bytes_per_sample(waveform_flags))
+ {
+ case 1:
+ *((INT8 *) dest_ptr) = interpolate8(sum);
+ break;
+ case 2:
+ word = interpolate16(sum);
+ if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP)
+ word = FLIPENDIAN_INT16(word);
+ *((INT16 *) dest_ptr) = word;
+ break;
+ case 4:
+ dword = sum;
+ if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP)
+ dword = FLIPENDIAN_INT32(dword);
+ *((INT32 *) dest_ptr) = dword;
+ break;
+ }
+ }
+ return CASSETTE_ERROR_SUCCESS;
+}
+
+
+
+casserr_t cassette_put_samples(cassette_image *cassette, int channel,
+ double time_index, double sample_period, size_t sample_count, size_t sample_bytes,
+ const void *samples, int waveform_flags)
+{
+ casserr_t err;
+ struct manipulation_ranges ranges;
+ size_t sample_index;
+ INT32 *dest_ptr;
+ INT32 dest_value;
+ INT16 word;
+ INT32 dword;
+ const UINT8 *source_ptr;
+ double d;
+
+ if (!cassette)
+ return CASSETTE_ERROR_SUCCESS;
+
+ if (sample_period == 0)
+ return CASSETTE_ERROR_SUCCESS;
+
+ err = compute_manipulation_ranges(cassette, channel, time_index, sample_period, &ranges);
+ if (err)
+ return err;
+
+ if (cassette->sample_count < ranges.sample_last+1)
+ cassette->sample_count = ranges.sample_last + 1;
+ cassette->flags |= CASSETTE_FLAG_DIRTY;
+
+ if (LOG_PUT_SAMPLES)
+ {
+ logerror("cassette_put_samples(): Putting samples TIME=[%2.6g..%2.6g] INDEX=[%i..%i]\n",
+ time_index, time_index + sample_period,
+ (int)ranges.sample_first, (int)ranges.sample_last);
+ }
+
+ for (sample_index = ranges.sample_first; sample_index <= ranges.sample_last; sample_index++)
+ {
+ /* figure out the source pointer */
+ d = map_double(sample_count, ranges.sample_first, ranges.sample_last + 1, sample_index);
+ source_ptr = (const UINT8*)samples;
+ source_ptr += ((size_t) d) * sample_bytes;
+
+ /* compute the value that we are writing */
+ switch(waveform_bytes_per_sample(waveform_flags)) {
+ case 1:
+ dest_value = extrapolate8(*((INT8 *) source_ptr));
+ break;
+ case 2:
+ word = *((INT16 *) source_ptr);
+ if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP)
+ word = FLIPENDIAN_INT16(word);
+ dest_value = extrapolate16(word);
+ break;
+ case 4:
+ dword = *((INT32 *) source_ptr);
+ if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP)
+ dword = FLIPENDIAN_INT32(dword);
+ dest_value = dword;
+ break;
+ default:
+ return CASSETTE_ERROR_INTERNAL;
+ }
+
+ for (channel = ranges.channel_first; channel <= ranges.channel_last; channel++)
+ {
+ /* find the sample that we are putting */
+ err = lookup_sample(cassette, channel, sample_index, TRUE, &dest_ptr);
+ if (err)
+ return err;
+ *dest_ptr = dest_value;
+ }
+ }
+ return CASSETTE_ERROR_SUCCESS;
+}
+
+
+
+casserr_t cassette_get_sample(cassette_image *cassette, int channel,
+ double time_index, double sample_period, INT32 *sample)
+{
+ return cassette_get_samples(cassette, channel, time_index,
+ sample_period, 1, 0, sample, CASSETTE_WAVEFORM_32BIT);
+}
+
+
+
+casserr_t cassette_put_sample(cassette_image *cassette, int channel,
+ double time_index, double sample_period, INT32 sample)
+{
+ return cassette_put_samples(cassette, channel, time_index,
+ sample_period, 1, 0, &sample, CASSETTE_WAVEFORM_32BIT);
+}
+
+
+
+/*********************************************************************
+ waveform accesses to/from the raw image
+*********************************************************************/
+
+casserr_t cassette_read_samples(cassette_image *cassette, int channels, double time_index,
+ double sample_period, size_t sample_count, UINT64 offset, int waveform_flags)
+{
+ casserr_t err;
+ size_t chunk_sample_count;
+ size_t bytes_per_sample;
+ size_t sample_bytes;
+ size_t samples_loaded = 0;
+ double chunk_time_index;
+ double chunk_sample_period;
+ int channel;
+ UINT8 buffer[8192];
+
+ bytes_per_sample = waveform_bytes_per_sample(waveform_flags);
+ sample_bytes = bytes_per_sample * channels;
+
+ while(samples_loaded < sample_count)
+ {
+ chunk_sample_count = MIN(sizeof(buffer) / sample_bytes, (sample_count - samples_loaded));
+ chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count);
+ chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_loaded);
+
+ cassette_image_read(cassette, buffer, offset, chunk_sample_count * sample_bytes);
+
+ for (channel = 0; channel < channels; channel++)
+ {
+ err = cassette_put_samples(cassette, channel, chunk_time_index, chunk_sample_period,
+ chunk_sample_count, sample_bytes, &buffer[channel * bytes_per_sample], waveform_flags);
+ if (err)
+ return err;
+ }
+
+ offset += chunk_sample_count * sample_bytes;
+ samples_loaded += chunk_sample_count;
+ }
+ return CASSETTE_ERROR_SUCCESS;
+}
+
+
+
+casserr_t cassette_write_samples(cassette_image *cassette, int channels, double time_index,
+ double sample_period, size_t sample_count, UINT64 offset, int waveform_flags)
+{
+ casserr_t err;
+ size_t chunk_sample_count;
+ size_t bytes_per_sample;
+ size_t sample_bytes;
+ size_t samples_saved = 0;
+ double chunk_time_index;
+ double chunk_sample_period;
+ int channel;
+ UINT8 buffer[8192];
+
+ bytes_per_sample = waveform_bytes_per_sample(waveform_flags);
+ sample_bytes = bytes_per_sample * channels;
+
+ while(samples_saved < sample_count)
+ {
+ chunk_sample_count = MIN(sizeof(buffer) / sample_bytes, (sample_count - samples_saved));
+ chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count);
+ chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_saved);
+
+ for (channel = 0; channel < channels; channel++)
+ {
+ err = cassette_get_samples(cassette, channel, chunk_time_index, chunk_sample_period,
+ chunk_sample_count, sample_bytes, &buffer[channel * bytes_per_sample], waveform_flags);
+ if (err)
+ return err;
+ }
+
+ cassette_image_write(cassette, buffer, offset, chunk_sample_count * sample_bytes);
+
+ offset += chunk_sample_count * sample_bytes;
+ samples_saved += chunk_sample_count;
+ }
+ return CASSETTE_ERROR_SUCCESS;
+}
+
+
+
+/*********************************************************************
+ waveform accesses to/from the raw image
+*********************************************************************/
+
+static const INT8 *choose_wave(const struct CassetteModulation *modulation, size_t *wave_bytes_length)
+{
+ static const INT8 square_wave[] = { -128, 127 };
+ static const INT8 sine_wave[] = { 0, 48, 89, 117, 127, 117, 89, 48, 0, -48, -89, -117, -127, -117, -89, -48 };
+
+ if (modulation->flags & CASSETTE_MODULATION_SINEWAVE)
+ {
+ *wave_bytes_length = ARRAY_LENGTH(sine_wave);
+ return sine_wave;
+ }
+ else
+ {
+ *wave_bytes_length = ARRAY_LENGTH(square_wave);
+ return square_wave;
+ }
+}
+
+
+
+casserr_t cassette_modulation_identify(cassette_image *cassette, const struct CassetteModulation *modulation,
+ struct CassetteOptions *opts)
+{
+ size_t wave_bytes_length;
+ choose_wave(modulation, &wave_bytes_length);
+ opts->bits_per_sample = 8;
+ opts->channels = 1;
+ opts->sample_frequency = (UINT32) (MAX(modulation->zero_frequency_high, modulation->one_frequency_high) * wave_bytes_length * 2);
+ return CASSETTE_ERROR_SUCCESS;
+}
+
+
+
+casserr_t cassette_put_modulated_data(cassette_image *cassette, int channel, double time_index,
+ const void *data, size_t data_length, const struct CassetteModulation *modulation,
+ double *time_displacement)
+{
+ casserr_t err;
+ const UINT8 *data_bytes = (const UINT8 *)data;
+ const INT8 *wave_bytes;
+ size_t wave_bytes_length;
+ double total_displacement = 0.0;
+ double pulse_period;
+ double pulse_frequency;
+ UINT8 b;
+ int i;
+
+ wave_bytes = choose_wave(modulation, &wave_bytes_length);
+
+ while(data_length--)
+ {
+ b = *(data_bytes++);
+ for (i = 0; i < 8; i++)
+ {
+ pulse_frequency = (b & (1 << i)) ? modulation->one_frequency_cannonical : modulation->zero_frequency_cannonical;
+ pulse_period = 1 / pulse_frequency;
+ err = cassette_put_samples(cassette, 0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, CASSETTE_WAVEFORM_8BIT);
+ if (err)
+ goto done;
+ time_index += pulse_period;
+ total_displacement += pulse_period;
+ }
+ }
+ err = CASSETTE_ERROR_SUCCESS;
+
+done:
+ if (time_displacement)
+ *time_displacement = total_displacement;
+ return err;
+}
+
+
+
+casserr_t cassette_put_modulated_filler(cassette_image *cassette, int channel, double time_index,
+ UINT8 filler, size_t filler_length, const struct CassetteModulation *modulation,
+ double *time_displacement)
+{
+ casserr_t err;
+ double delta;
+ double total_displacement = 0.0;
+
+ while(filler_length--)
+ {
+ err = cassette_put_modulated_data(cassette, channel, time_index, &filler, 1, modulation, &delta);
+ if (err)
+ return err;
+ total_displacement += delta;
+ time_index += delta;
+ }
+
+ if (time_displacement)
+ *time_displacement = total_displacement;
+ return CASSETTE_ERROR_SUCCESS;
+}
+
+
+
+casserr_t cassette_read_modulated_data(cassette_image *cassette, int channel, double time_index,
+ UINT64 offset, UINT64 length, const struct CassetteModulation *modulation,
+ double *time_displacement)
+{
+ casserr_t err;
+ UINT8 buffer_stack[1024];
+ UINT8 *buffer;
+ UINT8 *alloc_buffer = NULL;
+ double delta;
+ double total_displacement = 0.0;
+ size_t this_length;
+ size_t buffer_length;
+
+ if (length <= sizeof(buffer_stack))
+ {
+ buffer = buffer_stack;
+ buffer_length = sizeof(buffer_stack);
+ }
+ else
+ {
+ buffer_length = MIN(length, 100000);
+ alloc_buffer = (UINT8*)malloc(buffer_length);
+ if (!alloc_buffer)
+ {
+ err = CASSETTE_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ buffer = alloc_buffer;
+ }
+
+ while(length > 0)
+ {
+ this_length = (size_t) MIN(length, buffer_length);
+ cassette_image_read(cassette, buffer, offset, this_length);
+
+ err = cassette_put_modulated_data(cassette, channel, time_index, buffer, this_length, modulation, &delta);
+ if (err)
+ goto done;
+ total_displacement += delta;
+ time_index += delta;
+ length -= this_length;
+ }
+
+ if (time_displacement)
+ *time_displacement = total_displacement;
+ err = CASSETTE_ERROR_SUCCESS;
+
+done:
+ if (alloc_buffer)
+ free(alloc_buffer);
+ return err;
+}
+
+
+
+casserr_t cassette_put_modulated_data_bit(cassette_image *cassette, int channel, double time_index,
+ UINT8 data, const struct CassetteModulation *modulation,
+ double *time_displacement)
+{
+ casserr_t err;
+ const INT8 *wave_bytes;
+ size_t wave_bytes_length;
+ double total_displacement = 0.0;
+ double pulse_period;
+ double pulse_frequency;
+
+ wave_bytes = choose_wave(modulation, &wave_bytes_length);
+
+ pulse_frequency = (data) ? modulation->one_frequency_cannonical : modulation->zero_frequency_cannonical;
+ pulse_period = 1 / pulse_frequency;
+ err = cassette_put_samples(cassette, 0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, CASSETTE_WAVEFORM_8BIT);
+ if (err)
+ goto done;
+ time_index += pulse_period;
+ total_displacement += pulse_period;
+
+ err = CASSETTE_ERROR_SUCCESS;
+
+done:
+ if (time_displacement)
+ *time_displacement = total_displacement;
+ return err;
+}
+
+
+
+/*********************************************************************
+ waveform accesses to/from the raw image
+*********************************************************************/
+
+casserr_t cassette_legacy_identify(cassette_image *cassette, struct CassetteOptions *opts,
+ const struct CassetteLegacyWaveFiller *legacy_args)
+{
+ opts->channels = 1;
+ opts->bits_per_sample = 16;
+ opts->sample_frequency = legacy_args->sample_frequency;
+ return CASSETTE_ERROR_SUCCESS;
+}
+
+
+
+casserr_t cassette_legacy_construct(cassette_image *cassette,
+ const struct CassetteLegacyWaveFiller *legacy_args)
+{
+ casserr_t err;
+ int length;
+ int sample_count;
+ void *bytes = NULL;
+ void *chunk = NULL;
+ INT16 *samples = NULL;
+ int pos = 0;
+ UINT64 offset = 0;
+ UINT64 size;
+ struct CassetteLegacyWaveFiller args;
+
+ /* sanity check the args */
+ assert(legacy_args->header_samples >= -1);
+ assert(legacy_args->trailer_samples >= 0);
+ assert(legacy_args->fill_wave);
+
+ size = cassette_image_size(cassette);
+
+ /* normalize the args */
+ args = *legacy_args;
+ if (args.chunk_size == 0)
+ args.chunk_size = 1;
+ else if (args.chunk_size < 0)
+ args.chunk_size = cassette_image_size(cassette);
+ if (args.sample_frequency == 0)
+ args.sample_frequency = 11025;
+
+ /* allocate a buffer for the binary data */
+ chunk = malloc(args.chunk_size);
+ if (!chunk)
+ {
+ err = CASSETTE_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* determine number of samples */
+ if (args.chunk_sample_calc)
+ {
+ if (size > 0x7FFFFFFF)
+ {
+ err = CASSETTE_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ bytes = malloc(size);
+ if (!bytes)
+ {
+ err = CASSETTE_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ cassette_image_read(cassette, bytes, 0, size);
+ sample_count = args.chunk_sample_calc((const UINT8*)bytes, (int) size);
+
+ if (args.header_samples < 0)
+ args.header_samples = sample_count;
+ }
+ else
+ {
+ sample_count = ((size + args.chunk_size - 1) / args.chunk_size)
+ * args.chunk_samples;
+ }
+ sample_count += args.header_samples + args.trailer_samples;
+
+ /* allocate a buffer for the completed samples */
+ samples = (INT16*)malloc(sample_count * sizeof(*samples));
+ if (!samples)
+ {
+ err = CASSETTE_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* if there has to be a header */
+ if (args.header_samples > 0)
+ {
+ length = args.fill_wave(samples + pos, sample_count - pos, CODE_HEADER);
+ if (length < 0)
+ {
+ err = CASSETTE_ERROR_INVALIDIMAGE;
+ goto done;
+ }
+ pos += length;
+ }
+
+ /* convert the file data to samples */
+ while((pos < sample_count) && (offset < size))
+ {
+ cassette_image_read(cassette, chunk, offset, args.chunk_size);
+ offset += args.chunk_size;
+
+ length = args.fill_wave(samples + pos, sample_count - pos, (UINT8*)chunk);
+ if (length < 0)
+ {
+ err = CASSETTE_ERROR_INVALIDIMAGE;
+ goto done;
+ }
+ pos += length;
+ if (length == 0)
+ break;
+ }
+
+ /* if there has to be a trailer */
+ if (args.trailer_samples > 0)
+ {
+ length = args.fill_wave(samples + pos, sample_count - pos, CODE_TRAILER);
+ if (length < 0)
+ {
+ err = CASSETTE_ERROR_INVALIDIMAGE;
+ goto done;
+ }
+ pos += length;
+ }
+
+ /* specify the wave */
+ err = cassette_put_samples(cassette, 0, 0.0, ((double) pos) / args.sample_frequency,
+ pos, 2, samples, CASSETTE_WAVEFORM_16BIT);
+ if (err)
+ goto done;
+
+ /* success! */
+ err = CASSETTE_ERROR_SUCCESS;
+
+#if DUMP_CASSETTES
+ cassette_dump(cassette, "C:\\TEMP\\CASDUMP.WAV");
+#endif
+
+done:
+ if (samples)
+ free(samples);
+ if (chunk)
+ free(chunk);
+ if (bytes)
+ free(bytes);
+ return err;
+}
+
+
+
+/*********************************************************************
+ cassette_dump
+
+ A debugging call to dump a casette image to a disk based wave file
+*********************************************************************/
+
+void cassette_dump(cassette_image *image, const char *filename)
+{
+ FILE *f;
+ struct io_generic saved_io;
+ const struct CassetteFormat *saved_format;
+
+ f = fopen(filename, "wb");
+ if (!f)
+ return;
+
+ memcpy(&saved_io, &image->io, sizeof(saved_io));
+ saved_format = image->format;
+
+ image->io.file = f;
+ image->io.procs = &stdio_ioprocs_noclose;
+ image->format = &wavfile_format;
+ cassette_perform_save(image);
+
+ memcpy(&image->io, &saved_io, sizeof(saved_io));
+ image->format = saved_format;
+
+ fclose(f);
+}
+
+
diff --git a/src/emu/imagedev/cassimg.h b/src/emu/imagedev/cassimg.h
new file mode 100644
index 00000000000..5128e5628d2
--- /dev/null
+++ b/src/emu/imagedev/cassimg.h
@@ -0,0 +1,227 @@
+/*********************************************************************
+
+ cassimg.h
+
+ Cassette tape image abstraction code
+
+*********************************************************************/
+
+#ifndef CASSIMG_H
+#define CASSIMG_H
+
+#include "pool.h"
+#include "emu.h"
+
+/***************************************************************************
+
+ Constants
+
+***************************************************************************/
+
+#define CASSETTE_FLAG_READWRITE 0
+#define CASSETTE_FLAG_READONLY 1
+#define CASSETTE_FLAG_NOSAVEONEXIT 0
+#define CASSETTE_FLAG_SAVEONEXIT 2
+
+#define CASSETTE_WAVEFORM_8BIT 0
+#define CASSETTE_WAVEFORM_16BIT 2
+#define CASSETTE_WAVEFORM_16BIT_FLIP 3
+#define CASSETTE_WAVEFORM_32BIT 4
+#define CASSETTE_WAVEFORM_32BIT_FLIP 5
+#define CASSETTE_WAVEFORM_ENDIAN_FLIP 1
+
+#define CASSETTE_MODULATION_SQUAREWAVE 0
+#define CASSETTE_MODULATION_SINEWAVE 1
+
+
+#ifdef LSB_FIRST
+#define CASSETTE_WAVEFORM_16BITBE CASSETTE_WAVEFORM_16BIT_FLIP
+#define CASSETTE_WAVEFORM_16BITLE CASSETTE_WAVEFORM_16BIT
+#define CASSETTE_WAVEFORM_32BITBE CASSETTE_WAVEFORM_32BIT_FLIP
+#define CASSETTE_WAVEFORM_32BITLE CASSETTE_WAVEFORM_32BIT
+#else
+#define CASSETTE_WAVEFORM_16BITBE CASSETTE_WAVEFORM_16BIT
+#define CASSETTE_WAVEFORM_16BITLE CASSETTE_WAVEFORM_16BIT_FLIP
+#define CASSETTE_WAVEFORM_32BITBE CASSETTE_WAVEFORM_32BIT
+#define CASSETTE_WAVEFORM_32BITLE CASSETTE_WAVEFORM_32BIT_FLIP
+#endif
+
+
+/***************************************************************************
+
+ Type definitions
+
+***************************************************************************/
+
+typedef enum
+{
+ CASSETTE_ERROR_SUCCESS, /* no error */
+ CASSETTE_ERROR_INTERNAL, /* fatal internal error */
+ CASSETTE_ERROR_UNSUPPORTED, /* this operation is unsupported */
+ CASSETTE_ERROR_OUTOFMEMORY, /* ran out of memory */
+ CASSETTE_ERROR_INVALIDIMAGE, /* invalid image */
+ CASSETTE_ERROR_INVALIDARG
+}
+casserr_t;
+
+struct sample_block
+{
+ INT32 *block;
+ size_t sample_count;
+};
+
+struct CassetteOptions
+{
+ int channels;
+ int bits_per_sample;
+ UINT32 sample_frequency;
+};
+
+struct CassetteInfo
+{
+ int channels;
+ int bits_per_sample;
+ UINT32 sample_frequency;
+ size_t sample_count;
+};
+
+struct _cassette_image
+{
+ const struct CassetteFormat *format;
+ struct io_generic io;
+ object_pool *pool;
+
+ int channels;
+ int flags;
+ UINT32 sample_frequency;
+
+ struct sample_block *blocks;
+ size_t block_count;
+ size_t sample_count;
+
+ running_machine *machine;
+};
+
+typedef struct _cassette_image cassette_image;
+
+struct CassetteFormat
+{
+ const char *extensions;
+ casserr_t (*identify)(cassette_image *cassette, struct CassetteOptions *opts);
+ casserr_t (*load)(cassette_image *cassette);
+ casserr_t (*save)(cassette_image *cassette, const struct CassetteInfo *info);
+};
+
+/* used for the core modulation code */
+struct CassetteModulation
+{
+ int flags;
+ double zero_frequency_low;
+ double zero_frequency_cannonical;
+ double zero_frequency_high;
+ double one_frequency_low;
+ double one_frequency_cannonical;
+ double one_frequency_high;
+};
+
+/* code to adapt existing legacy fill_wave functions */
+struct CassetteLegacyWaveFiller
+{
+ int (*fill_wave)(INT16 *, int, UINT8 *);
+ int chunk_size;
+ int chunk_samples;
+ int (*chunk_sample_calc)(const UINT8 *bytes, int length);
+ UINT32 sample_frequency;
+ int header_samples;
+ int trailer_samples;
+};
+
+/* builtin formats */
+extern const struct CassetteFormat wavfile_format;
+
+/* macros for specifying format lists */
+#define CASSETTE_FORMATLIST_EXTERN(name) \
+ extern const struct CassetteFormat *const name[]
+
+#define CASSETTE_FORMATLIST_START(name) \
+ const struct CassetteFormat *const name[] = \
+ { \
+ &wavfile_format, \
+
+#define CASSETTE_FORMAT(name) \
+ &(name), \
+
+#define CASSETTE_FORMATLIST_END \
+ NULL \
+ };
+
+CASSETTE_FORMATLIST_EXTERN(cassette_default_formats);
+
+/***************************************************************************
+
+ Prototypes
+
+***************************************************************************/
+
+casserr_t cassette_open(running_machine *machine,void *file, const struct io_procs *procs,
+ const struct CassetteFormat *format, int flags, cassette_image **outcassette);
+casserr_t cassette_open_choices(running_machine *machine,void *file, const struct io_procs *procs, const char *extension,
+ const struct CassetteFormat *const *formats, int flags, cassette_image **outcassette);
+casserr_t cassette_create(running_machine *machine,void *file, const struct io_procs *procs, const struct CassetteFormat *format,
+ const struct CassetteOptions *opts, int flags, cassette_image **outcassette);
+casserr_t cassette_save(cassette_image *cassette);
+void cassette_close(cassette_image *cassette);
+void cassette_change(cassette_image *cassette, void *file, const struct io_procs *procs, const struct CassetteFormat *format, int flags);
+void cassette_get_info(cassette_image *cassette, struct CassetteInfo *info);
+
+/* calls for accessing the raw cassette image */
+void cassette_image_read(cassette_image *cassette, void *buffer, UINT64 offset, size_t length);
+void cassette_image_write(cassette_image *cassette, const void *buffer, UINT64 offset, size_t length);
+UINT64 cassette_image_size(cassette_image *cassette);
+
+/* waveform accesses */
+casserr_t cassette_get_samples(cassette_image *cassette, int channel,
+ double time_index, double sample_period, size_t sample_count, size_t sample_bytes,
+ void *samples, int waveform_flags);
+casserr_t cassette_put_samples(cassette_image *cassette, int channel,
+ double time_index, double sample_period, size_t sample_count, size_t sample_bytes,
+ const void *samples, int waveform_flags);
+casserr_t cassette_get_sample(cassette_image *cassette, int channel,
+ double time_index, double sample_period, INT32 *sample);
+casserr_t cassette_put_sample(cassette_image *cassette, int channel,
+ double time_index, double sample_period, INT32 sample);
+
+/* waveform accesses to/from the raw image */
+casserr_t cassette_read_samples(cassette_image *cassette, int channels, double time_index,
+ double sample_period, size_t sample_count, UINT64 offset, int waveform_flags);
+casserr_t cassette_write_samples(cassette_image *cassette, int channels, double time_index,
+ double sample_period, size_t sample_count, UINT64 offset, int waveform_flags);
+
+/* modulation support */
+casserr_t cassette_modulation_identify(cassette_image *cassette, const struct CassetteModulation *modulation,
+ struct CassetteOptions *opts);
+casserr_t cassette_put_modulated_data(cassette_image *cassette, int channel, double time_index,
+ const void *data, size_t data_length, const struct CassetteModulation *modulation,
+ double *time_displacement);
+casserr_t cassette_put_modulated_filler(cassette_image *cassette, int channel, double time_index,
+ UINT8 filler, size_t filler_length, const struct CassetteModulation *modulation,
+ double *time_displacement);
+casserr_t cassette_read_modulated_data(cassette_image *cassette, int channel, double time_index,
+ UINT64 offset, UINT64 length, const struct CassetteModulation *modulation,
+ double *time_displacement);
+casserr_t cassette_put_modulated_data_bit(cassette_image *cassette, int channel, double time_index,
+ UINT8 data, const struct CassetteModulation *modulation,
+ double *time_displacement);
+
+/* debug calls */
+void cassette_dump(cassette_image *image, const char *filename);
+
+/* legacy code support */
+#define CODE_HEADER ((UINT8*)-1)
+#define CODE_TRAILER ((UINT8*)-2)
+casserr_t cassette_legacy_identify(cassette_image *cassette, struct CassetteOptions *opts,
+ const struct CassetteLegacyWaveFiller *legacy_args);
+casserr_t cassette_legacy_construct(cassette_image *cassette,
+ const struct CassetteLegacyWaveFiller *legacy_args);
+
+#endif /* CASSIMG_H */
diff --git a/src/emu/imagedev/chd_cd.c b/src/emu/imagedev/chd_cd.c
new file mode 100644
index 00000000000..46eb1a6365b
--- /dev/null
+++ b/src/emu/imagedev/chd_cd.c
@@ -0,0 +1,191 @@
+/*********************************************************************
+
+ Code to interface the MESS image code with MAME's CHD-CD core.
+
+ Based on harddriv.c by Raphael Nabet 2003
+
+*********************************************************************/
+
+#include "emu.h"
+#include "cdrom.h"
+#include "chd_cd.h"
+
+
+static const char *const error_strings[] =
+{
+ "no error",
+ "no drive interface",
+ "out of memory",
+ "invalid file",
+ "invalid parameter",
+ "invalid data",
+ "file not found",
+ "requires parent",
+ "file not writeable",
+ "read error",
+ "write error",
+ "codec error",
+ "invalid parent",
+ "hunk out of range",
+ "decompression error",
+ "compression error",
+ "can't create file",
+ "can't verify file"
+ "operation not supported",
+ "can't find metadata",
+ "invalid metadata size",
+ "unsupported CHD version"
+};
+
+INLINE const cdrom_config *get_config_dev(const device_config *device)
+{
+ assert(device != NULL);
+ assert(device->type() == CDROM);
+ return (const cdrom_config *)downcast<const legacy_device_config_base *>(device)->inline_config();
+}
+
+static const char *chd_get_error_string(int chderr)
+{
+ if ((chderr < 0 ) || (chderr >= ARRAY_LENGTH(error_strings)))
+ return NULL;
+ return error_strings[chderr];
+}
+
+
+static OPTION_GUIDE_START(mess_cd_option_guide)
+ OPTION_INT('K', "hunksize", "Hunk Bytes")
+OPTION_GUIDE_END
+
+static const char mess_cd_option_spec[] =
+ "K512/1024/2048/[4096]";
+
+
+typedef struct _dev_cdrom_t dev_cdrom_t;
+struct _dev_cdrom_t
+{
+ cdrom_file *cdrom_handle;
+};
+
+
+INLINE dev_cdrom_t *get_safe_token(device_t *device) {
+ assert( device != NULL );
+ assert( ( device->type() == CDROM ) );
+ return (dev_cdrom_t *) downcast<legacy_device_base *>(device)->token();
+}
+
+
+static DEVICE_IMAGE_LOAD(cdrom)
+{
+ dev_cdrom_t *cdrom = get_safe_token(&image.device());
+ chd_error err = (chd_error)0;
+ chd_file *chd = NULL;
+
+ err = chd_open_file( image.image_core_file(), CHD_OPEN_READ, NULL, &chd ); /* CDs are never writeable */
+ if ( err )
+ goto error;
+
+ /* open the CHD file */
+ cdrom->cdrom_handle = cdrom_open( chd );
+ if ( ! cdrom->cdrom_handle )
+ goto error;
+
+ return IMAGE_INIT_PASS;
+
+error:
+ if ( chd )
+ chd_close( chd );
+ if ( err )
+ image.seterror( IMAGE_ERROR_UNSPECIFIED, chd_get_error_string( err ) );
+ return IMAGE_INIT_FAIL;
+}
+
+
+static DEVICE_IMAGE_UNLOAD(cdrom)
+{
+ dev_cdrom_t *cdrom = get_safe_token( &image.device() );
+
+ assert( cdrom->cdrom_handle );
+ cdrom_close( cdrom->cdrom_handle );
+ cdrom->cdrom_handle = NULL;
+}
+
+
+/*************************************
+ *
+ * Get the MESS/MAME cdrom handle (from the src/cdrom.c core)
+ * after an image has been opened with the mess_cd core
+ *
+ *************************************/
+
+cdrom_file *mess_cd_get_cdrom_file(device_t *image)
+{
+ dev_cdrom_t *cdrom = get_safe_token( image );
+
+ return cdrom->cdrom_handle;
+}
+
+
+/*-------------------------------------------------
+ DEVICE_START(cdrom)
+-------------------------------------------------*/
+
+static DEVICE_START(cdrom)
+{
+ dev_cdrom_t *cdrom = get_safe_token( device );
+
+ cdrom->cdrom_handle = NULL;
+}
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_SOFTLIST_LOAD(cdrom)
+-------------------------------------------------*/
+static DEVICE_IMAGE_SOFTLIST_LOAD(cdrom)
+{
+ return image.load_software(swlist, swname, start_entry);
+}
+
+/*-------------------------------------------------
+ DEVICE_GET_INFO(cdrom)
+-------------------------------------------------*/
+
+DEVICE_GET_INFO(cdrom)
+{
+ switch( state )
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(dev_cdrom_t); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(cdrom_config); break;
+ case DEVINFO_INT_IMAGE_TYPE: info->i = IO_CDROM; break;
+ case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 0; break;
+ case DEVINFO_INT_IMAGE_CREATABLE: info->i = 0; break;
+ case DEVINFO_INT_IMAGE_CREATE_OPTCOUNT: info->i = 1; break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(cdrom); break;
+ case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(cdrom); break;
+ case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(cdrom); break;
+ case DEVINFO_FCT_IMAGE_SOFTLIST_LOAD: info->f = (genf *) DEVICE_IMAGE_SOFTLIST_LOAD_NAME(cdrom); break;
+ case DEVINFO_PTR_IMAGE_CREATE_OPTGUIDE: info->p = (void *) mess_cd_option_guide; break;
+ case DEVINFO_PTR_IMAGE_CREATE_OPTSPEC+0: info->p = (void *) mess_cd_option_spec; break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Cdrom"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Cdrom"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, "chd"); break;
+ case DEVINFO_STR_IMAGE_CREATE_OPTNAME+0: strcpy(info->s, "chdcd"); break;
+ case DEVINFO_STR_IMAGE_CREATE_OPTDESC+0: strcpy(info->s, "MAME/MESS CHD CD-ROM drive"); break;
+ case DEVINFO_STR_IMAGE_CREATE_OPTEXTS+0: strcpy(info->s, "chd"); break;
+
+ case DEVINFO_STR_IMAGE_INTERFACE:
+ if ( device && downcast<const legacy_image_device_config_base *>(device)->inline_config() && get_config_dev(device)->interface )
+ {
+ strcpy(info->s, get_config_dev(device)->interface );
+ }
+ break;
+
+ }
+}
+
+DEFINE_LEGACY_IMAGE_DEVICE(CDROM, cdrom);
diff --git a/src/emu/imagedev/chd_cd.h b/src/emu/imagedev/chd_cd.h
new file mode 100644
index 00000000000..e6603b61e90
--- /dev/null
+++ b/src/emu/imagedev/chd_cd.h
@@ -0,0 +1,43 @@
+/*********************************************************************
+
+ chd_cd.h
+
+ MESS interface to the MAME CHD CDROM code
+
+*********************************************************************/
+
+#ifndef MESS_CD_H
+#define MESS_CD_H
+
+#include "cdrom.h"
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct cdrom_config_t cdrom_config;
+struct cdrom_config_t
+{
+ const char * interface;
+};
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+DECLARE_LEGACY_IMAGE_DEVICE(CDROM, cdrom);
+
+
+cdrom_file *mess_cd_get_cdrom_file(device_t *device);
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+
+#define MCFG_CDROM_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, CDROM, 0) \
+
+#define MCFG_CDROM_INTERFACE(_interface) \
+ MCFG_DEVICE_CONFIG_DATAPTR(cdrom_config, interface, _interface )
+
+#endif /* MESS_CD_H */
diff --git a/src/emu/imagedev/flopdrv.c b/src/emu/imagedev/flopdrv.c
new file mode 100644
index 00000000000..8295839a1df
--- /dev/null
+++ b/src/emu/imagedev/flopdrv.c
@@ -0,0 +1,1045 @@
+/*
+ This code handles the floppy drives.
+ All FDD actions should be performed using these functions.
+
+ The functions are emulated and a disk image is used.
+
+ Real disk operation:
+ - set unit id
+
+ TODO:
+ - Override write protect if disk image has been opened in read mode
+*/
+
+#include "emu.h"
+#include "imageutl.h"
+#include "flopdrv.h"
+
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define FLOPDRVTAG "flopdrv"
+#define LOG_FLOPPY 0
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _floppy_drive floppy_drive;
+struct _floppy_drive
+{
+ /* callbacks */
+ devcb_resolved_write_line out_idx_func;
+ devcb_resolved_read_line in_mon_func;
+ devcb_resolved_write_line out_tk00_func;
+ devcb_resolved_write_line out_wpt_func;
+ devcb_resolved_write_line out_rdy_func;
+ devcb_resolved_write_line out_dskchg_func;
+
+ /* state of input lines */
+ int drtn; /* direction */
+ int stp; /* step */
+ int wtg; /* write gate */
+ int mon; /* motor on */
+
+ /* state of output lines */
+ int idx; /* index pulse */
+ int tk00; /* track 00 */
+ int wpt; /* write protect */
+ int rdy; /* ready */
+ int dskchg; /* disk changed */
+
+ /* drive select logic */
+ int drive_id;
+ int active;
+
+ const floppy_config *config;
+
+ /* flags */
+ int flags;
+ /* maximum track allowed */
+ int max_track;
+ /* num sides */
+ int num_sides;
+ /* current track - this may or may not relate to the present cylinder number
+ stored by the fdc */
+ int current_track;
+
+ /* index pulse timer */
+ void *index_timer;
+ /* index pulse callback */
+ void (*index_pulse_callback)(device_t *controller,device_t *image, int state);
+ /* rotation per minute => gives index pulse frequency */
+ float rpm;
+
+ void (*ready_state_change_callback)(device_t *controller,device_t *img, int state);
+
+ int id_index;
+
+ device_t *controller;
+
+ floppy_image *floppy;
+ int track;
+ void (*load_proc)(device_image_interface &image);
+ void (*unload_proc)(device_image_interface &image);
+ void *custom_data;
+ int floppy_drive_type;
+};
+
+
+typedef struct _floppy_error_map floppy_error_map;
+struct _floppy_error_map
+{
+ floperr_t ferr;
+ image_error_t ierr;
+ const char *message;
+};
+
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+static const floppy_error_map errmap[] =
+{
+ { FLOPPY_ERROR_SUCCESS, IMAGE_ERROR_SUCCESS },
+ { FLOPPY_ERROR_INTERNAL, IMAGE_ERROR_INTERNAL },
+ { FLOPPY_ERROR_UNSUPPORTED, IMAGE_ERROR_UNSUPPORTED },
+ { FLOPPY_ERROR_OUTOFMEMORY, IMAGE_ERROR_OUTOFMEMORY },
+ { FLOPPY_ERROR_INVALIDIMAGE, IMAGE_ERROR_INVALIDIMAGE }
+};
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+DECLARE_LEGACY_IMAGE_DEVICE(FLOPPY_APPLE, apple525);
+DECLARE_LEGACY_IMAGE_DEVICE(FLOPPY_SONY, sonydriv);
+
+INLINE floppy_drive *get_safe_token(device_t *device)
+{
+ assert( device != NULL );
+ assert( device->type() == FLOPPY || device->type() == FLOPPY_APPLE || device->type() == FLOPPY_SONY);
+ return (floppy_drive *) downcast<legacy_device_base *>(device)->token();
+}
+
+floppy_image *flopimg_get_image(device_t *image)
+{
+ return get_safe_token(image)->floppy;
+}
+
+void *flopimg_get_custom_data(device_t *image)
+{
+ floppy_drive *flopimg = get_safe_token( image );
+ return flopimg->custom_data;
+}
+
+void flopimg_alloc_custom_data(device_t *image,void *custom)
+{
+ floppy_drive *flopimg = get_safe_token( image );
+ flopimg->custom_data = custom;
+}
+
+static int flopimg_get_sectors_per_track(device_t *image, int side)
+{
+ floperr_t err;
+ int sector_count;
+ floppy_drive *flopimg = get_safe_token( image );
+
+ if (!flopimg || !flopimg->floppy)
+ return 0;
+
+ err = floppy_get_sector_count(flopimg->floppy, side, flopimg->track, &sector_count);
+ if (err)
+ return 0;
+ return sector_count;
+}
+
+static void flopimg_get_id_callback(device_t *image, chrn_id *id, int id_index, int side)
+{
+ floppy_drive *flopimg;
+ int cylinder, sector, N;
+ unsigned long flags;
+ UINT32 sector_length;
+
+ flopimg = get_safe_token( image );
+ if (!flopimg || !flopimg->floppy)
+ return;
+
+ floppy_get_indexed_sector_info(flopimg->floppy, side, flopimg->track, id_index, &cylinder, &side, &sector, &sector_length, &flags);
+
+ N = compute_log2(sector_length);
+
+ id->C = cylinder;
+ id->H = side;
+ id->R = sector;
+ id->data_id = id_index;
+ id->flags = flags;
+ id->N = ((N >= 7) && (N <= 10)) ? N - 7 : 0;
+}
+
+static void log_readwrite(const char *name, int head, int track, int sector, const char *buf, int length)
+{
+ char membuf[1024];
+ int i;
+ for (i = 0; i < length; i++)
+ sprintf(membuf + i*2, "%02x", (int) (UINT8) buf[i]);
+ logerror("%s: head=%i track=%i sector=%i buffer='%s'\n", name, head, track, sector, membuf);
+}
+
+static void floppy_drive_set_geometry_absolute(device_t *img, int tracks, int sides)
+{
+ floppy_drive *pDrive = get_safe_token( img );
+ pDrive->max_track = tracks;
+ pDrive->num_sides = sides;
+}
+
+void floppy_drive_set_geometry(device_t *img, floppy_type_t type)
+{
+ floppy_drive_set_geometry_absolute(img, type.max_track_number, type.head_number);
+}
+
+static TIMER_CALLBACK(floppy_drive_index_callback);
+
+/* this is called on device init */
+static void floppy_drive_init(device_t *img)
+{
+ floppy_drive *pDrive = get_safe_token( img );
+
+ /* initialise flags */
+ pDrive->flags = 0;
+ pDrive->index_pulse_callback = NULL;
+ pDrive->ready_state_change_callback = NULL;
+ pDrive->index_timer = timer_alloc(img->machine, floppy_drive_index_callback, (void *) img);
+ pDrive->idx = 0;
+
+ floppy_drive_set_geometry(img, ((floppy_config*)img->baseconfig().static_config())->floppy_type);
+
+ /* initialise id index - not so important */
+ pDrive->id_index = 0;
+ /* initialise track */
+ pDrive->current_track = 0;
+
+ /* default RPM */
+ pDrive->rpm = 300;
+
+ pDrive->controller = NULL;
+
+ pDrive->custom_data = NULL;
+
+ pDrive->floppy_drive_type = FLOPPY_TYPE_REGULAR;
+}
+
+/* index pulses at rpm/60 Hz, and stays high 1/20th of time */
+static void floppy_drive_index_func(device_t *img)
+{
+ floppy_drive *drive = get_safe_token( img );
+
+ double ms = 1000.0 / (drive->rpm / 60.0);
+
+ if (drive->idx)
+ {
+ drive->idx = 0;
+ timer_adjust_oneshot((emu_timer*)drive->index_timer, double_to_attotime(ms*19/20/1000.0), 0);
+ }
+ else
+ {
+ drive->idx = 1;
+ timer_adjust_oneshot((emu_timer*)drive->index_timer, double_to_attotime(ms/20/1000.0), 0);
+ }
+
+ devcb_call_write_line(&drive->out_idx_func, drive->idx);
+
+ if (drive->index_pulse_callback)
+ drive->index_pulse_callback(drive->controller, img, drive->idx);
+}
+
+static TIMER_CALLBACK(floppy_drive_index_callback)
+{
+ device_t *image = (device_t *) ptr;
+ floppy_drive_index_func(image);
+}
+
+/*************************************************************************/
+/* IO_FLOPPY device functions */
+
+/* set flag state */
+void floppy_drive_set_flag_state(device_t *img, int flag, int state)
+{
+ floppy_drive *drv = get_safe_token( img );
+ int prev_state;
+ int new_state;
+
+ /* get old state */
+ prev_state = drv->flags & flag;
+
+ /* set new state */
+ drv->flags &= ~flag;
+ if (state)
+ drv->flags |= flag;
+
+ /* get new state */
+ new_state = drv->flags & flag;
+
+ /* changed state? */
+ if (prev_state ^ new_state)
+ {
+ if (flag & FLOPPY_DRIVE_READY)
+ {
+ /* trigger state change callback */
+ devcb_call_write_line(&drv->out_rdy_func, new_state ? ASSERT_LINE : CLEAR_LINE);
+
+ if (drv->ready_state_change_callback)
+ drv->ready_state_change_callback(drv->controller, img, new_state);
+ }
+ }
+}
+
+/* for pc, drive is always ready, for amstrad,pcw,spectrum it is only ready under
+a fixed set of circumstances */
+/* use this to set ready state of drive */
+void floppy_drive_set_ready_state(device_t *img, int state, int flag)
+{
+ floppy_drive *drive = get_safe_token(img);
+ device_image_interface *image = dynamic_cast<device_image_interface *>(img);
+ if (flag)
+ {
+ /* set ready only if drive is present, disk is in the drive,
+ and disk motor is on - for Amstrad, Spectrum and PCW*/
+
+ /* drive present? */
+ /* disk inserted? */
+ if (image->exists())
+ {
+ if (drive->mon == CLEAR_LINE)
+ {
+ /* set state */
+ floppy_drive_set_flag_state(img, FLOPPY_DRIVE_READY, state);
+ return;
+ }
+ }
+ floppy_drive_set_flag_state(img, FLOPPY_DRIVE_READY, 0);
+ }
+ else
+ {
+ /* force ready state - for PC driver */
+ floppy_drive_set_flag_state(img, FLOPPY_DRIVE_READY, state);
+ }
+}
+
+/* get flag state */
+int floppy_drive_get_flag_state(device_t *img, int flag)
+{
+ floppy_drive *drv = get_safe_token( img );
+ int drive_flags;
+ int flags;
+
+ flags = 0;
+
+ drive_flags = drv->flags;
+
+ /* these flags are independant of a real drive/disk image */
+ flags |= drive_flags & (FLOPPY_DRIVE_READY | FLOPPY_DRIVE_INDEX);
+
+ flags &= flag;
+
+ return flags;
+}
+
+
+void floppy_drive_seek(device_t *img, signed int signed_tracks)
+{
+ device_image_interface *image = dynamic_cast<device_image_interface *>(img);
+ floppy_drive *pDrive;
+
+ pDrive = get_safe_token( img );
+
+ LOG(("seek from: %d delta: %d\n",pDrive->current_track, signed_tracks));
+
+ /* update position */
+ pDrive->current_track+=signed_tracks;
+
+ if (pDrive->current_track<0)
+ {
+ pDrive->current_track = 0;
+ }
+ else
+ if (pDrive->current_track>=pDrive->max_track)
+ {
+ pDrive->current_track = pDrive->max_track-1;
+ }
+
+ /* set track 0 flag */
+ pDrive->tk00 = (pDrive->current_track == 0) ? CLEAR_LINE : ASSERT_LINE;
+ devcb_call_write_line(&pDrive->out_tk00_func, pDrive->tk00);
+
+ /* clear disk changed flag */
+ pDrive->dskchg = ASSERT_LINE;
+ //devcb_call_write_line(&flopimg->out_dskchg_func, flopimg->dskchg);
+
+ /* inform disk image of step operation so it can cache information */
+ if (image->exists())
+ pDrive->track = pDrive->current_track;
+
+ pDrive->id_index = 0;
+}
+
+
+/* this is not accurate. But it will do for now */
+int floppy_drive_get_next_id(device_t *img, int side, chrn_id *id)
+{
+ floppy_drive *pDrive;
+ int spt;
+
+ pDrive = get_safe_token( img );
+
+ /* get sectors per track */
+ spt = flopimg_get_sectors_per_track(img, side);
+
+ /* set index */
+ if ((pDrive->id_index==(spt-1)) || (spt==0))
+ {
+ floppy_drive_set_flag_state(img, FLOPPY_DRIVE_INDEX, 1);
+ }
+ else
+ {
+ floppy_drive_set_flag_state(img, FLOPPY_DRIVE_INDEX, 0);
+ }
+
+ /* get id */
+ if (spt!=0)
+ {
+ flopimg_get_id_callback(img, id, pDrive->id_index, side);
+ }
+
+ pDrive->id_index++;
+ if (spt!=0)
+ pDrive->id_index %= spt;
+ else
+ pDrive->id_index = 0;
+
+ return (spt == 0) ? 0 : 1;
+}
+
+void floppy_drive_read_track_data_info_buffer(device_t *img, int side, void *ptr, int *length )
+{
+ floppy_drive *flopimg;
+ device_image_interface *image = dynamic_cast<device_image_interface *>(img);
+
+ if (image->exists())
+ {
+ flopimg = get_safe_token( img );
+ if (!flopimg || !flopimg->floppy)
+ return;
+
+ floppy_read_track_data(flopimg->floppy, side, flopimg->track, ptr, *length);
+ }
+}
+
+void floppy_drive_write_track_data_info_buffer(device_t *img, int side, const void *ptr, int *length )
+{
+ floppy_drive *flopimg;
+ device_image_interface *image = dynamic_cast<device_image_interface *>(img);
+
+ if (image->exists())
+ {
+ flopimg = get_safe_token( img );
+ if (!flopimg || !flopimg->floppy)
+ return;
+
+ floppy_write_track_data(flopimg->floppy, side, flopimg->track, ptr, *length);
+ }
+}
+
+void floppy_drive_format_sector(device_t *img, int side, int sector_index,int c,int h, int r, int n, int filler)
+{
+ device_image_interface *image = dynamic_cast<device_image_interface *>(img);
+
+ if (image->exists())
+ {
+/* if (drv->interface_.format_sector)
+ drv->interface_.format_sector(img, side, sector_index,c, h, r, n, filler);*/
+ }
+}
+
+void floppy_drive_read_sector_data(device_t *img, int side, int index1, void *ptr, int length)
+{
+ floppy_drive *flopimg;
+ device_image_interface *image = dynamic_cast<device_image_interface *>(img);
+
+ if (image->exists())
+ {
+ flopimg = get_safe_token( img );
+ if (!flopimg || !flopimg->floppy)
+ return;
+
+ floppy_read_indexed_sector(flopimg->floppy, side, flopimg->track, index1, 0, ptr, length);
+
+ if (LOG_FLOPPY)
+ log_readwrite("sector_read", side, flopimg->track, index1, (const char *)ptr, length);
+
+ }
+}
+
+void floppy_drive_write_sector_data(device_t *img, int side, int index1, const void *ptr,int length, int ddam)
+{
+ floppy_drive *flopimg;
+ device_image_interface *image = dynamic_cast<device_image_interface *>(img);
+
+ if (image->exists())
+ {
+ flopimg = get_safe_token( img );
+ if (!flopimg || !flopimg->floppy)
+ return;
+
+ if (LOG_FLOPPY)
+ log_readwrite("sector_write", side, flopimg->track, index1, (const char *)ptr, length);
+
+ floppy_write_indexed_sector(flopimg->floppy, side, flopimg->track, index1, 0, ptr, length, ddam);
+ }
+}
+
+void floppy_install_load_proc(device_t *image, void (*proc)(device_image_interface &image))
+{
+ floppy_drive *flopimg = get_safe_token( image );
+ flopimg->load_proc = proc;
+}
+
+void floppy_install_unload_proc(device_t *image, void (*proc)(device_image_interface &image))
+{
+ floppy_drive *flopimg = get_safe_token( image );
+ flopimg->unload_proc = proc;
+}
+
+/* set the callback for the index pulse */
+void floppy_drive_set_index_pulse_callback(device_t *img, void (*callback)(device_t *controller,device_t *image, int state))
+{
+ floppy_drive *pDrive = get_safe_token( img );
+ pDrive->index_pulse_callback = callback;
+}
+
+
+void floppy_drive_set_ready_state_change_callback(device_t *img, void (*callback)(device_t *controller,device_t *img, int state))
+{
+ floppy_drive *pDrive = get_safe_token( img );
+ pDrive->ready_state_change_callback = callback;
+}
+
+int floppy_drive_get_current_track(device_t *img)
+{
+ floppy_drive *drv = get_safe_token( img );
+ return drv->current_track;
+}
+
+UINT64 floppy_drive_get_current_track_size(device_t *img, int head)
+{
+ floppy_drive *drv = get_safe_token( img );
+ int size = 0;
+ device_image_interface *image = dynamic_cast<device_image_interface *>(img);
+
+ if (image->exists())
+ {
+ size = floppy_get_track_size(drv->floppy, head, drv->current_track);
+ }
+
+ return size;
+}
+
+void floppy_drive_set_rpm(device_t *img, float rpm)
+{
+ floppy_drive *drv = get_safe_token( img );
+ drv->rpm = rpm;
+}
+
+void floppy_drive_set_controller(device_t *img, device_t *controller)
+{
+ floppy_drive *drv = get_safe_token( img );
+ drv->controller = controller;
+}
+
+/* ----------------------------------------------------------------------- */
+DEVICE_START( floppy )
+{
+ floppy_drive *floppy = get_safe_token( device );
+ floppy->config = (const floppy_config*)device->baseconfig().static_config();
+ floppy_drive_init(device);
+
+ floppy->drive_id = floppy_get_drive(device);
+ floppy->active = FALSE;
+
+ /* resolve callbacks */
+ devcb_resolve_write_line(&floppy->out_idx_func, &floppy->config->out_idx_func, device);
+ devcb_resolve_read_line(&floppy->in_mon_func, &floppy->config->in_mon_func, device);
+ devcb_resolve_write_line(&floppy->out_tk00_func, &floppy->config->out_tk00_func, device);
+ devcb_resolve_write_line(&floppy->out_wpt_func, &floppy->config->out_wpt_func, device);
+ devcb_resolve_write_line(&floppy->out_rdy_func, &floppy->config->out_rdy_func, device);
+// devcb_resolve_write_line(&floppy->out_dskchg_func, &floppy->config->out_dskchg_func, device);
+
+ /* by default we are not write-protected */
+ floppy->wpt = ASSERT_LINE;
+ devcb_call_write_line(&floppy->out_wpt_func, floppy->wpt);
+
+ /* not at track 0 */
+ floppy->tk00 = ASSERT_LINE;
+ devcb_call_write_line(&floppy->out_tk00_func, floppy->tk00);
+
+ /* motor off */
+ floppy->mon = ASSERT_LINE;
+
+ /* disk changed */
+ floppy->dskchg = CLEAR_LINE;
+// devcb_call_write_line(&floppy->out_dskchg_func, floppy->dskchg);
+}
+
+static int internal_floppy_device_load(device_image_interface *image, int create_format, option_resolution *create_args)
+{
+ floperr_t err;
+ floppy_drive *flopimg;
+ const struct FloppyFormat *floppy_options;
+ int floppy_flags, i;
+ const char *extension;
+
+ /* look up instance data */
+ flopimg = get_safe_token( &image->device() );
+
+ /* figure out the floppy options */
+ floppy_options = ((floppy_config*)image->device().baseconfig().static_config())->formats;
+
+ if (image->has_been_created())
+ {
+ /* creating an image */
+ assert(create_format >= 0);
+ err = floppy_create((void *) image, &image_ioprocs, &floppy_options[create_format], create_args, &flopimg->floppy);
+ if (err)
+ goto error;
+ }
+ else
+ {
+ /* opening an image */
+ floppy_flags = image->is_writable() ? FLOPPY_FLAGS_READWRITE : FLOPPY_FLAGS_READONLY;
+ extension = image->filetype();
+ err = floppy_open_choices((void *) image, &image_ioprocs, extension, floppy_options, floppy_flags, &flopimg->floppy);
+ if (err)
+ goto error;
+ }
+ if (floppy_callbacks(flopimg->floppy)->get_heads_per_disk && floppy_callbacks(flopimg->floppy)->get_tracks_per_disk)
+ {
+ floppy_drive_set_geometry_absolute(&image->device(),
+ floppy_get_tracks_per_disk(flopimg->floppy),
+ floppy_get_heads_per_disk(flopimg->floppy));
+ }
+ return IMAGE_INIT_PASS;
+
+error:
+ for (i = 0; i < ARRAY_LENGTH(errmap); i++)
+ {
+ if (err == errmap[i].ferr)
+ image->seterror(errmap[i].ierr, errmap[i].message);
+ }
+ return IMAGE_INIT_FAIL;
+}
+
+static TIMER_CALLBACK( set_wpt )
+{
+ floppy_drive *flopimg = (floppy_drive *)ptr;
+
+ flopimg->wpt = param;
+ devcb_call_write_line(&flopimg->out_wpt_func, param);
+}
+
+DEVICE_IMAGE_LOAD( floppy )
+{
+ floppy_drive *flopimg;
+ int retVal = internal_floppy_device_load(&image, -1, NULL);
+ flopimg = get_safe_token( &image.device() );
+ if (retVal==IMAGE_INIT_PASS) {
+ /* if we have one of our hacky unload procs, call it */
+ if (flopimg->load_proc)
+ flopimg->load_proc(image);
+ }
+
+ /* push disk halfway into drive */
+ flopimg->wpt = CLEAR_LINE;
+ devcb_call_write_line(&flopimg->out_wpt_func, flopimg->wpt);
+
+ /* set timer for disk load */
+ int next_wpt;
+
+ if (image.is_writable())
+ next_wpt = ASSERT_LINE;
+ else
+ next_wpt = CLEAR_LINE;
+
+ timer_set(image.device().machine, ATTOTIME_IN_MSEC(250), flopimg, next_wpt, set_wpt);
+
+ return retVal;
+}
+
+DEVICE_IMAGE_CREATE( floppy )
+{
+ return internal_floppy_device_load(&image, create_format, create_args);
+}
+
+DEVICE_IMAGE_UNLOAD( floppy )
+{
+ floppy_drive *flopimg = get_safe_token( &image.device() );
+ if (flopimg->unload_proc)
+ flopimg->unload_proc(image);
+
+ floppy_close(flopimg->floppy);
+ flopimg->floppy = NULL;
+
+ /* disk changed */
+ flopimg->dskchg = CLEAR_LINE;
+ //devcb_call_write_line(&flopimg->out_dskchg_func, flopimg->dskchg);
+
+ /* pull disk halfway out of drive */
+ flopimg->wpt = CLEAR_LINE;
+ devcb_call_write_line(&flopimg->out_wpt_func, flopimg->wpt);
+
+ /* set timer for disk eject */
+ timer_set(image.device().machine, ATTOTIME_IN_MSEC(250), flopimg, ASSERT_LINE, set_wpt);
+}
+
+device_t *floppy_get_device(running_machine *machine,int drive)
+{
+ switch(drive) {
+ case 0 : return machine->device(FLOPPY_0);
+ case 1 : return machine->device(FLOPPY_1);
+ case 2 : return machine->device(FLOPPY_2);
+ case 3 : return machine->device(FLOPPY_3);
+ }
+ return NULL;
+}
+
+int floppy_get_drive_type(device_t *image)
+{
+ floppy_drive *flopimg = get_safe_token( image );
+ return flopimg->floppy_drive_type;
+}
+
+void floppy_set_type(device_t *image,int ftype)
+{
+ floppy_drive *flopimg = get_safe_token( image );
+ flopimg->floppy_drive_type = ftype;
+}
+
+device_t *floppy_get_device_by_type(running_machine *machine,int ftype,int drive)
+{
+ int i;
+ int cnt = 0;
+ for (i=0;i<4;i++) {
+ device_t *disk = floppy_get_device(machine,i);
+ if (floppy_get_drive_type(disk)==ftype) {
+ if (cnt==drive) {
+ return disk;
+ }
+ cnt++;
+ }
+ }
+ return NULL;
+}
+
+int floppy_get_drive(device_t *image)
+{
+ int drive =0;
+ if (strcmp(image->tag(), FLOPPY_0) == 0) drive = 0;
+ if (strcmp(image->tag(), FLOPPY_1) == 0) drive = 1;
+ if (strcmp(image->tag(), FLOPPY_2) == 0) drive = 2;
+ if (strcmp(image->tag(), FLOPPY_3) == 0) drive = 3;
+ return drive;
+}
+
+int floppy_get_drive_by_type(device_t *image,int ftype)
+{
+ int i,drive =0;
+ for (i=0;i<4;i++) {
+ device_t *disk = floppy_get_device(image->machine,i);
+ if (floppy_get_drive_type(disk)==ftype) {
+ if (image==disk) {
+ return drive;
+ }
+ drive++;
+ }
+ }
+ return drive;
+}
+
+int floppy_get_count(running_machine *machine)
+{
+ int cnt = 0;
+ if (machine->device(FLOPPY_0)) cnt++;
+ if (machine->device(FLOPPY_1)) cnt++;
+ if (machine->device(FLOPPY_2)) cnt++;
+ if (machine->device(FLOPPY_3)) cnt++;
+ return cnt;
+}
+
+
+/* drive select 0 */
+WRITE_LINE_DEVICE_HANDLER( floppy_ds0_w )
+{
+ floppy_drive *drive = get_safe_token(device);
+
+ if (state == CLEAR_LINE)
+ drive->active = (drive->drive_id == 0);
+}
+
+/* drive select 1 */
+WRITE_LINE_DEVICE_HANDLER( floppy_ds1_w )
+{
+ floppy_drive *drive = get_safe_token(device);
+
+ if (state == CLEAR_LINE)
+ drive->active = (drive->drive_id == 1);
+}
+
+/* drive select 2 */
+WRITE_LINE_DEVICE_HANDLER( floppy_ds2_w )
+{
+ floppy_drive *drive = get_safe_token(device);
+
+ if (state == CLEAR_LINE)
+ drive->active = (drive->drive_id == 2);
+}
+
+/* drive select 3 */
+WRITE_LINE_DEVICE_HANDLER( floppy_ds3_w )
+{
+ floppy_drive *drive = get_safe_token(device);
+
+ if (state == CLEAR_LINE)
+ drive->active = (drive->drive_id == 3);
+}
+
+/* shortcut to write all four ds lines */
+WRITE8_DEVICE_HANDLER( floppy_ds_w )
+{
+ floppy_ds0_w(device, BIT(data, 0));
+ floppy_ds1_w(device, BIT(data, 1));
+ floppy_ds2_w(device, BIT(data, 2));
+ floppy_ds3_w(device, BIT(data, 3));
+}
+
+/* motor on, active low */
+WRITE_LINE_DEVICE_HANDLER( floppy_mon_w )
+{
+ floppy_drive *drive = get_safe_token(device);
+ device_image_interface *image = dynamic_cast<device_image_interface *>(device);
+ /* force off if there is no attached image */
+ if (!image->exists())
+ state = ASSERT_LINE;
+
+ /* off -> on */
+ if (drive->mon && state == CLEAR_LINE)
+ {
+ drive->idx = 0;
+ floppy_drive_index_func(device);
+ }
+
+ /* on -> off */
+ else if (drive->mon == CLEAR_LINE && state)
+ timer_adjust_oneshot((emu_timer*)drive->index_timer, attotime_zero, 0);
+
+ drive->mon = state;
+}
+
+/* direction */
+WRITE_LINE_DEVICE_HANDLER( floppy_drtn_w )
+{
+ floppy_drive *drive = get_safe_token(device);
+ drive->drtn = state;
+}
+
+/* write data */
+WRITE_LINE_DEVICE_HANDLER( floppy_wtd_w )
+{
+
+}
+
+/* step */
+WRITE_LINE_DEVICE_HANDLER( floppy_stp_w )
+{
+ floppy_drive *drive = get_safe_token(device);
+
+ /* move head one track when going from high to low and write gate is high */
+ if (drive->active && drive->stp && state == CLEAR_LINE && drive->wtg)
+ {
+ /* move head according to the direction line */
+ if (drive->drtn)
+ {
+ /* move head outward */
+ if (drive->current_track > 0)
+ drive->current_track--;
+
+ /* are we at track 0 now? */
+ drive->tk00 = (drive->current_track == 0) ? CLEAR_LINE : ASSERT_LINE;
+ }
+ else
+ {
+ /* move head inward */
+ if (drive->current_track < drive->max_track)
+ drive->current_track++;
+
+ /* we can't be at track 0 here, so reset the line */
+ drive->tk00 = ASSERT_LINE;
+ }
+
+ /* update track 0 line with new status */
+ devcb_call_write_line(&drive->out_tk00_func, drive->tk00);
+ }
+
+ drive->stp = state;
+}
+
+/* write gate */
+WRITE_LINE_DEVICE_HANDLER( floppy_wtg_w )
+{
+ floppy_drive *drive = get_safe_token(device);
+ drive->wtg = state;
+}
+
+/* write protect signal, active low */
+READ_LINE_DEVICE_HANDLER( floppy_wpt_r )
+{
+ floppy_drive *drive = get_safe_token(device);
+ return drive->wpt;
+}
+
+/* track 0 detect */
+READ_LINE_DEVICE_HANDLER( floppy_tk00_r )
+{
+ floppy_drive *drive = get_safe_token(device);
+ return drive->tk00;
+}
+
+/* disk changed */
+READ_LINE_DEVICE_HANDLER( floppy_dskchg_r )
+{
+ floppy_drive *drive = get_safe_token(device);
+ return drive->dskchg;
+}
+
+/* 2-sided disk */
+READ_LINE_DEVICE_HANDLER( floppy_twosid_r )
+{
+ floppy_drive *drive = get_safe_token(device);
+
+ if (drive->floppy == NULL)
+ return ASSERT_LINE;
+ else
+ return !floppy_get_heads_per_disk(drive->floppy);
+}
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_SOFTLIST_LOAD(floppy)
+-------------------------------------------------*/
+static DEVICE_IMAGE_SOFTLIST_LOAD(floppy)
+{
+ return image.load_software(swlist, swname, start_entry);
+}
+
+/*************************************
+ *
+ * Device specification function
+ *
+ *************************************/
+/*-------------------------------------------------
+ safe_strcpy - hack
+-------------------------------------------------*/
+
+static void safe_strcpy(char *dst, const char *src)
+{
+ strcpy(dst, src ? src : "");
+}
+
+DEVICE_GET_INFO(floppy)
+{
+ switch( state )
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(floppy_drive); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+ case DEVINFO_INT_IMAGE_TYPE: info->i = IO_FLOPPY; break;
+ case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_CREATABLE:
+ {
+ int cnt = 0;
+ if ( device && device->static_config() )
+ {
+ const struct FloppyFormat *floppy_options = ((floppy_config*)device->static_config())->formats;
+ int i;
+ for ( i = 0; floppy_options[i].construct; i++ ) {
+ if(floppy_options[i].param_guidelines) cnt++;
+ }
+ }
+ info->i = (cnt>0) ? 1 : 0;
+ }
+ break;
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(floppy); break;
+ case DEVINFO_FCT_IMAGE_CREATE: info->f = (genf *) DEVICE_IMAGE_CREATE_NAME(floppy); break;
+ case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(floppy); break;
+ case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(floppy); break;
+ case DEVINFO_FCT_IMAGE_SOFTLIST_LOAD: info->f = (genf *) DEVICE_IMAGE_SOFTLIST_LOAD_NAME(floppy); break;
+ case DEVINFO_PTR_IMAGE_CREATE_OPTGUIDE: info->p = (void *)floppy_option_guide; break;
+ case DEVINFO_INT_IMAGE_CREATE_OPTCOUNT:
+ {
+ const struct FloppyFormat *floppy_options = ((floppy_config*)device->static_config())->formats;
+ int count;
+ for (count = 0; floppy_options[count].construct; count++)
+ ;
+ info->i = count;
+ break;
+ }
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Floppy Disk"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Floppy Disk"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_IMAGE_FILE_EXTENSIONS:
+ if ( device && device->static_config() )
+ {
+ const struct FloppyFormat *floppy_options = ((floppy_config*)device->static_config())->formats;
+ int i;
+ /* set up a temporary string */
+ info->s[0] = '\0';
+ for ( i = 0; floppy_options[i].construct; i++ )
+ image_specify_extension( info->s, 256, floppy_options[i].extensions );
+ }
+ break;
+ case DEVINFO_STR_IMAGE_INTERFACE:
+ if ( device && device->static_config() && ((floppy_config *)device->static_config())->interface)
+ {
+ strcpy(info->s, ((floppy_config *)device->static_config())->interface );
+ }
+ break;
+ default:
+ {
+ if ( device && device->static_config() )
+ {
+ const struct FloppyFormat *floppy_options = ((floppy_config*)device->static_config())->formats;
+ if ((state >= DEVINFO_PTR_IMAGE_CREATE_OPTSPEC) && (state < DEVINFO_PTR_IMAGE_CREATE_OPTSPEC + DEVINFO_IMAGE_CREATE_OPTMAX)) {
+ info->p = (void *) floppy_options[state - DEVINFO_PTR_IMAGE_CREATE_OPTSPEC].param_guidelines;
+ } else if ((state >= DEVINFO_STR_IMAGE_CREATE_OPTNAME) && (state < DEVINFO_STR_IMAGE_CREATE_OPTNAME + DEVINFO_IMAGE_CREATE_OPTMAX)) {
+ safe_strcpy(info->s,floppy_options[state - DEVINFO_STR_IMAGE_CREATE_OPTNAME].name);
+ }
+ else if ((state >= DEVINFO_STR_IMAGE_CREATE_OPTDESC) && (state < DEVINFO_STR_IMAGE_CREATE_OPTDESC + DEVINFO_IMAGE_CREATE_OPTMAX)) {
+ safe_strcpy(info->s,floppy_options[state - DEVINFO_STR_IMAGE_CREATE_OPTDESC].description);
+ }
+ else if ((state >= DEVINFO_STR_IMAGE_CREATE_OPTEXTS) && (state < DEVINFO_STR_IMAGE_CREATE_OPTEXTS + DEVINFO_IMAGE_CREATE_OPTMAX)) {
+ safe_strcpy(info->s,floppy_options[state - DEVINFO_STR_IMAGE_CREATE_OPTEXTS].extensions);
+ }
+ }
+ }
+
+ break;
+ }
+}
+
+DEFINE_LEGACY_IMAGE_DEVICE(FLOPPY, floppy);
diff --git a/src/emu/imagedev/flopdrv.h b/src/emu/imagedev/flopdrv.h
new file mode 100644
index 00000000000..971b5ee1ac1
--- /dev/null
+++ b/src/emu/imagedev/flopdrv.h
@@ -0,0 +1,245 @@
+/* flopdrv provides simple emulation of a disc drive */
+/* the 8271, upd765 and wd179x use this */
+
+#ifndef __FLOPDRV_H__
+#define __FLOPDRV_H__
+
+#include "devcb.h"
+#include "image.h"
+#include "flopimg.h"
+
+#define FLOPPY_TYPE_REGULAR 0
+#define FLOPPY_TYPE_APPLE 1
+#define FLOPPY_TYPE_SONY 2
+
+#define FLOPPY_DRIVE_2_8_INCH 1
+#define FLOPPY_DRIVE_3_INCH 2
+#define FLOPPY_DRIVE_3_5_INCH 3
+#define FLOPPY_DRIVE_5_25_INCH 4
+#define FLOPPY_DRIVE_8_INCH 5
+
+// Maximum supported density
+#define FLOPPY_DRIVE_SD 1
+#define FLOPPY_DRIVE_DD 2
+#define FLOPPY_DRIVE_QD 3
+#define FLOPPY_DRIVE_HD 4
+#define FLOPPY_DRIVE_ED 5
+
+#define FLOPPY_STANDARD_3_SSDD { FLOPPY_DRIVE_3_INCH, 1, 42, FLOPPY_DRIVE_DD }
+#define FLOPPY_STANDARD_3_DSDD { FLOPPY_DRIVE_3_INCH, 2, 42, FLOPPY_DRIVE_DD }
+#define FLOPPY_STANDARD_3_5_DSDD { FLOPPY_DRIVE_3_5_INCH, 2, 83, FLOPPY_DRIVE_DD }
+#define FLOPPY_STANDARD_3_5_DSHD { FLOPPY_DRIVE_3_5_INCH, 2, 83, FLOPPY_DRIVE_HD }
+#define FLOPPY_STANDARD_5_25_SSSD_35 { FLOPPY_DRIVE_5_25_INCH, 1, 37, FLOPPY_DRIVE_SD }
+#define FLOPPY_STANDARD_5_25_DSSD_35 { FLOPPY_DRIVE_5_25_INCH, 2, 37, FLOPPY_DRIVE_SD }
+#define FLOPPY_STANDARD_5_25_SSSD { FLOPPY_DRIVE_5_25_INCH, 1, 42, FLOPPY_DRIVE_SD }
+#define FLOPPY_STANDARD_5_25_DSSD { FLOPPY_DRIVE_5_25_INCH, 2, 42, FLOPPY_DRIVE_SD }
+#define FLOPPY_STANDARD_5_25_SSDD_40 { FLOPPY_DRIVE_5_25_INCH, 1, 42, FLOPPY_DRIVE_DD }
+#define FLOPPY_STANDARD_5_25_DSDD_40 { FLOPPY_DRIVE_5_25_INCH, 2, 42, FLOPPY_DRIVE_DD }
+#define FLOPPY_STANDARD_5_25_SSDD { FLOPPY_DRIVE_5_25_INCH, 1, 83, FLOPPY_DRIVE_DD }
+#define FLOPPY_STANDARD_5_25_DSDD { FLOPPY_DRIVE_5_25_INCH, 2, 83, FLOPPY_DRIVE_DD }
+#define FLOPPY_STANDARD_5_25_DSQD { FLOPPY_DRIVE_5_25_INCH, 2, 83, FLOPPY_DRIVE_QD }
+#define FLOPPY_STANDARD_5_25_DSHD { FLOPPY_DRIVE_5_25_INCH, 2, 83, FLOPPY_DRIVE_HD }
+#define FLOPPY_STANDARD_8_SSSD { FLOPPY_DRIVE_8_INCH, 1, 77, FLOPPY_DRIVE_SD }
+#define FLOPPY_STANDARD_8_DSSD { FLOPPY_DRIVE_8_INCH, 2, 77, FLOPPY_DRIVE_SD }
+#define FLOPPY_STANDARD_8_DSDD { FLOPPY_DRIVE_8_INCH, 2, 77, FLOPPY_DRIVE_DD }
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+typedef struct floppy_type_t floppy_type;
+struct floppy_type_t
+{
+ UINT8 media_size;
+ UINT8 head_number;
+ UINT8 max_track_number;
+ UINT8 max_density;
+};
+
+typedef struct floppy_config_t floppy_config;
+struct floppy_config_t
+{
+ devcb_write_line out_idx_func; /* index */
+ devcb_read_line in_mon_func; /* motor on */
+ devcb_write_line out_tk00_func; /* track 00 */
+ devcb_write_line out_wpt_func; /* write protect */
+ devcb_write_line out_rdy_func; /* ready */
+// devcb_write_line out_dskchg_func; /* disk changed */
+
+ floppy_type_t floppy_type;
+ const struct FloppyFormat *formats;
+ const char *interface;
+};
+
+/* sector has a deleted data address mark */
+#define ID_FLAG_DELETED_DATA 0x0001
+/* CRC error in id field */
+#define ID_FLAG_CRC_ERROR_IN_ID_FIELD 0x0002
+/* CRC error in data field */
+#define ID_FLAG_CRC_ERROR_IN_DATA_FIELD 0x0004
+
+typedef struct chrn_id
+{
+ unsigned char C;
+ unsigned char H;
+ unsigned char R;
+ unsigned char N;
+ int data_id; // id for read/write data command
+ unsigned long flags;
+} chrn_id;
+
+/* set if drive is ready */
+#define FLOPPY_DRIVE_READY 0x0010
+/* set if index has just occured */
+#define FLOPPY_DRIVE_INDEX 0x0020
+
+/* a callback which will be executed if the ready state of the drive changes e.g. not ready->ready, ready->not ready */
+void floppy_drive_set_ready_state_change_callback(device_t *img, void (*callback)(device_t *controller,device_t *img, int state));
+
+void floppy_drive_set_index_pulse_callback(device_t *img, void (*callback)(device_t *controller,device_t *image, int state));
+
+/* set flag state */
+int floppy_drive_get_flag_state(device_t *img, int flag);
+/* get flag state */
+void floppy_drive_set_flag_state(device_t *img, int flag, int state);
+/* get current physical track drive is on */
+int floppy_drive_get_current_track(device_t *img);
+/* get current physical track size */
+UINT64 floppy_drive_get_current_track_size(device_t *img, int head);
+
+/* get next id from track, 1 if got a id, 0 if no id was got */
+int floppy_drive_get_next_id(device_t *img, int side, chrn_id *);
+/* set ready state of drive. If flag == 1, set ready state only if drive present,
+disk is in drive, and motor is on. Otherwise set ready state to the state passed */
+void floppy_drive_set_ready_state(device_t *img, int state, int flag);
+
+/* seek up or down */
+void floppy_drive_seek(device_t *img, signed int signed_tracks);
+
+void floppy_drive_read_track_data_info_buffer(device_t *img, int side, void *ptr, int *length );
+void floppy_drive_write_track_data_info_buffer(device_t *img, int side, const void *ptr, int *length );
+void floppy_drive_format_sector(device_t *img, int side, int sector_index, int c, int h, int r, int n, int filler);
+void floppy_drive_read_sector_data(device_t *img, int side, int index1, void *pBuffer, int length);
+void floppy_drive_write_sector_data(device_t *img, int side, int index1, const void *pBuffer, int length, int ddam);
+
+/* set motor speed to get correct index pulses
+ standard RPM are 300 RPM (common) and 360 RPM
+ Note: this actually only works for soft sectored disks: one index pulse per
+ track.
+*/
+void floppy_drive_set_rpm(device_t *image, float rpm);
+
+void floppy_drive_set_controller(device_t *img, device_t *controller);
+
+floppy_image *flopimg_get_image(device_t *image);
+
+/* hack for apple II; replace this when we think of something better */
+void floppy_install_unload_proc(device_t *image, void (*proc)(device_image_interface &image));
+
+void floppy_install_load_proc(device_t *image, void (*proc)(device_image_interface &image));
+
+device_t *floppy_get_device(running_machine *machine,int drive);
+device_t *floppy_get_device_by_type(running_machine *machine,int ftype,int drive);
+int floppy_get_drive_type(device_t *image);
+void floppy_set_type(device_t *image,int ftype);
+int floppy_get_count(running_machine *machine);
+
+int floppy_get_drive(device_t *image);
+int floppy_get_drive_by_type(device_t *image,int ftype);
+
+void *flopimg_get_custom_data(device_t *image);
+void flopimg_alloc_custom_data(device_t *image,void *custom);
+
+void floppy_drive_set_geometry(device_t *img, floppy_type_t type);
+
+/* drive select lines */
+WRITE_LINE_DEVICE_HANDLER( floppy_ds0_w );
+WRITE_LINE_DEVICE_HANDLER( floppy_ds1_w );
+WRITE_LINE_DEVICE_HANDLER( floppy_ds2_w );
+WRITE_LINE_DEVICE_HANDLER( floppy_ds3_w );
+WRITE8_DEVICE_HANDLER( floppy_ds_w );
+
+WRITE_LINE_DEVICE_HANDLER( floppy_mon_w );
+WRITE_LINE_DEVICE_HANDLER( floppy_drtn_w );
+WRITE_LINE_DEVICE_HANDLER( floppy_stp_w );
+WRITE_LINE_DEVICE_HANDLER( floppy_wtd_w );
+WRITE_LINE_DEVICE_HANDLER( floppy_wtg_w );
+
+/* write-protect */
+READ_LINE_DEVICE_HANDLER( floppy_wpt_r );
+
+/* track 0 detect */
+READ_LINE_DEVICE_HANDLER( floppy_tk00_r );
+
+/* disk changed */
+READ_LINE_DEVICE_HANDLER( floppy_dskchg_r );
+
+/* 2-sided disk */
+READ_LINE_DEVICE_HANDLER( floppy_twosid_r );
+
+DECLARE_LEGACY_IMAGE_DEVICE(FLOPPY, floppy);
+
+extern DEVICE_START( floppy );
+extern DEVICE_IMAGE_LOAD( floppy );
+extern DEVICE_IMAGE_CREATE( floppy );
+extern DEVICE_IMAGE_UNLOAD( floppy );
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+#define FLOPPY_0 "floppy0"
+#define FLOPPY_1 "floppy1"
+#define FLOPPY_2 "floppy2"
+#define FLOPPY_3 "floppy3"
+
+
+#define MCFG_FLOPPY_DRIVE_ADD(_tag, _config) \
+ MCFG_DEVICE_ADD(_tag, FLOPPY, 0) \
+ MCFG_DEVICE_CONFIG(_config)
+
+#define MCFG_FLOPPY_DRIVE_MODIFY(_tag, _config) \
+ MCFG_DEVICE_MODIFY(_tag) \
+ MCFG_DEVICE_CONFIG(_config)
+
+#define MCFG_FLOPPY_4_DRIVES_ADD(_config) \
+ MCFG_DEVICE_ADD(FLOPPY_0, FLOPPY, 0) \
+ MCFG_DEVICE_CONFIG(_config) \
+ MCFG_DEVICE_ADD(FLOPPY_1, FLOPPY, 0) \
+ MCFG_DEVICE_CONFIG(_config) \
+ MCFG_DEVICE_ADD(FLOPPY_2, FLOPPY, 0) \
+ MCFG_DEVICE_CONFIG(_config) \
+ MCFG_DEVICE_ADD(FLOPPY_3, FLOPPY, 0) \
+ MCFG_DEVICE_CONFIG(_config)
+
+#define MCFG_FLOPPY_4_DRIVES_MODIFY(_config) \
+ MCFG_DEVICE_MODIFY(FLOPPY_0) \
+ MCFG_DEVICE_CONFIG(_config) \
+ MCFG_DEVICE_MODIFY(FLOPPY_1) \
+ MCFG_DEVICE_CONFIG(_config) \
+ MCFG_DEVICE_MODIFY(FLOPPY_2) \
+ MCFG_DEVICE_CONFIG(_config) \
+ MCFG_DEVICE_MODIFY(FLOPPY_3) \
+ MCFG_DEVICE_CONFIG(_config)
+
+#define MCFG_FLOPPY_4_DRIVES_REMOVE() \
+ MCFG_DEVICE_REMOVE(FLOPPY_0) \
+ MCFG_DEVICE_REMOVE(FLOPPY_1) \
+ MCFG_DEVICE_REMOVE(FLOPPY_2) \
+ MCFG_DEVICE_REMOVE(FLOPPY_3)
+
+#define MCFG_FLOPPY_2_DRIVES_ADD(_config) \
+ MCFG_DEVICE_ADD(FLOPPY_0, FLOPPY, 0) \
+ MCFG_DEVICE_CONFIG(_config) \
+ MCFG_DEVICE_ADD(FLOPPY_1, FLOPPY, 0) \
+ MCFG_DEVICE_CONFIG(_config)
+
+#define MCFG_FLOPPY_2_DRIVES_MODIFY(_config) \
+ MCFG_DEVICE_MODIFY(FLOPPY_0) \
+ MCFG_DEVICE_CONFIG(_config) \
+ MCFG_DEVICE_MODIFY(FLOPPY_1) \
+ MCFG_DEVICE_CONFIG(_config)
+
+#define MCFG_FLOPPY_2_DRIVES_REMOVE() \
+ MCFG_DEVICE_REMOVE(FLOPPY_0) \
+ MCFG_DEVICE_REMOVE(FLOPPY_1)
+
+#endif /* __FLOPDRV_H__ */
diff --git a/src/emu/imagedev/flopimg.c b/src/emu/imagedev/flopimg.c
new file mode 100644
index 00000000000..eb3f3e4d01a
--- /dev/null
+++ b/src/emu/imagedev/flopimg.c
@@ -0,0 +1,930 @@
+/*********************************************************************
+
+ flopimg.c
+
+ Floppy disk image abstraction code
+
+*********************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "osdepend.h"
+#include "flopimg.h"
+#include "pool.h"
+#include "imageutl.h"
+
+#define TRACK_LOADED 0x01
+#define TRACK_DIRTY 0x02
+
+
+struct _floppy_image
+{
+ struct io_generic io;
+
+ const struct FloppyFormat *floppy_option;
+ struct FloppyCallbacks format;
+
+ /* loaded track stuff */
+ int loaded_track_head;
+ int loaded_track_index;
+ UINT32 loaded_track_size;
+ void *loaded_track_data;
+ UINT8 loaded_track_status;
+ UINT8 flags;
+
+ /* tagging system */
+ object_pool *tags;
+ void *tag_data;
+};
+
+
+
+struct _floppy_params
+{
+ int param;
+ int value;
+};
+
+
+
+static floperr_t floppy_track_unload(floppy_image *floppy);
+
+OPTION_GUIDE_START(floppy_option_guide)
+ OPTION_INT('H', "heads", "Heads")
+ OPTION_INT('T', "tracks", "Tracks")
+ OPTION_INT('S', "sectors", "Sectors")
+ OPTION_INT('L', "sectorlength", "Sector Bytes")
+ OPTION_INT('I', "interleave", "Interleave")
+ OPTION_INT('F', "firstsectorid", "First Sector")
+OPTION_GUIDE_END
+
+
+static void floppy_close_internal(floppy_image *floppy, int close_file);
+
+/*********************************************************************
+ opening, closing and creating of floppy images
+*********************************************************************/
+
+/* basic floppy_image initialization common to floppy_open() and floppy_create() */
+static floppy_image *floppy_init(void *fp, const struct io_procs *procs, int flags)
+{
+ floppy_image *floppy;
+
+ floppy = (floppy_image *)malloc(sizeof(struct _floppy_image));
+ if (!floppy)
+ return NULL;
+
+ memset(floppy, 0, sizeof(*floppy));
+ floppy->tags = pool_alloc_lib(NULL);
+ floppy->tag_data = NULL;
+ floppy->io.file = fp;
+ floppy->io.procs = procs;
+ floppy->io.filler = 0xFF;
+ floppy->flags = (UINT8) flags;
+ return floppy;
+}
+
+
+
+/* main code for identifying and maybe opening a disk image; not exposed
+ * directly because this function is big and hideous */
+static floperr_t floppy_open_internal(void *fp, const struct io_procs *procs, const char *extension,
+ const struct FloppyFormat *floppy_options, int max_options, int flags, floppy_image **outfloppy,
+ int *outoption)
+{
+ floperr_t err;
+ floppy_image *floppy;
+ int best_option = -1;
+ int best_vote = 0;
+ int vote;
+ size_t i;
+
+ floppy = floppy_init(fp, procs, flags);
+ if (!floppy)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* vote on the best format */
+ for (i = 0; (i < max_options) && floppy_options[i].construct; i++)
+ {
+ if (!extension || !floppy_options[i].extensions || image_find_extension(floppy_options[i].extensions, extension))
+ {
+ if (floppy_options[i].identify)
+ {
+ vote = 0;
+ err = floppy_options[i].identify(floppy, &floppy_options[i], &vote);
+ if (err)
+ goto done;
+ }
+ else
+ {
+ vote = 1;
+ }
+
+ /* is this option a better one? */
+ if (vote > best_vote)
+ {
+ best_vote = vote;
+ best_option = i;
+ }
+ }
+ }
+
+ /* did we find a format? */
+ if (best_option == -1)
+ {
+ err = FLOPPY_ERROR_INVALIDIMAGE;
+ goto done;
+ }
+
+ if (outfloppy)
+ {
+ /* call the format constructor */
+ err = floppy_options[best_option].construct(floppy, &floppy_options[best_option], NULL);
+ if (err)
+ goto done;
+
+ floppy->floppy_option = &floppy_options[best_option];
+ }
+
+ err = FLOPPY_ERROR_SUCCESS;
+
+done:
+ /* if we have a floppy disk and we either errored or are not keeping it, close it */
+ if (floppy && (!outfloppy || err))
+ {
+ floppy_close_internal(floppy, FALSE);
+ floppy = NULL;
+ }
+
+ if (outoption)
+ *outoption = err ? -1 : best_option;
+ if (outfloppy)
+ *outfloppy = floppy;
+ return err;
+}
+
+
+
+floperr_t floppy_identify(void *fp, const struct io_procs *procs, const char *extension,
+ const struct FloppyFormat *formats, int *identified_format)
+{
+ return floppy_open_internal(fp, procs, extension, formats, INT_MAX, FLOPPY_FLAGS_READONLY, NULL, identified_format);
+}
+
+
+
+floperr_t floppy_open(void *fp, const struct io_procs *procs, const char *extension,
+ const struct FloppyFormat *format, int flags, floppy_image **outfloppy)
+{
+ return floppy_open_internal(fp, procs, extension, format, 1, flags, outfloppy, NULL);
+}
+
+
+
+floperr_t floppy_open_choices(void *fp, const struct io_procs *procs, const char *extension,
+ const struct FloppyFormat *formats, int flags, floppy_image **outfloppy)
+{
+ return floppy_open_internal(fp, procs, extension, formats, INT_MAX, flags, outfloppy, NULL);
+}
+
+
+
+static floperr_t option_to_floppy_error(optreserr_t oerr)
+{
+ floperr_t err;
+ switch(oerr) {
+ case OPTIONRESOLUTION_ERROR_SUCCESS:
+ err = FLOPPY_ERROR_SUCCESS;
+ break;
+ case OPTIONRESOLUTION_ERROR_OUTOFMEMORY:
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ break;
+ case OPTIONRESOLUTION_ERROR_PARAMOUTOFRANGE:
+ case OPTIONRESOLUTION_ERROR_PARAMNOTSPECIFIED:
+ case OPTIONRESOLUTION_ERROR_PARAMNOTFOUND:
+ case OPTIONRESOLUTION_ERROR_PARAMALREADYSPECIFIED:
+ case OPTIONRESOLUTION_ERROR_BADPARAM:
+ case OPTIONRESOLUTION_ERROR_SYNTAX:
+ default:
+ err = FLOPPY_ERROR_INTERNAL;
+ break;
+ };
+ return err;
+}
+
+
+
+floperr_t floppy_create(void *fp, const struct io_procs *procs, const struct FloppyFormat *format, option_resolution *parameters, floppy_image **outfloppy)
+{
+ floppy_image *floppy = NULL;
+ optreserr_t oerr;
+ floperr_t err;
+ int heads, tracks, h, t;
+ option_resolution *alloc_resolution = NULL;
+
+ assert(format);
+
+ /* create the new image */
+ floppy = floppy_init(fp, procs, 0);
+ if (!floppy)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* if this format expects creation parameters and none were specified, create some */
+ if (!parameters && format->param_guidelines)
+ {
+ alloc_resolution = option_resolution_create(floppy_option_guide, format->param_guidelines);
+ if (!alloc_resolution)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ parameters = alloc_resolution;
+ }
+
+ /* finish the parameters, if specified */
+ if (parameters)
+ {
+ oerr = option_resolution_finish(parameters);
+ if (oerr)
+ {
+ err = option_to_floppy_error(oerr);
+ goto done;
+ }
+ }
+
+ /* call the format constructor */
+ err = format->construct(floppy, format, parameters);
+ if (err)
+ goto done;
+
+ /* format the disk, ignoring if formatting not implemented */
+ if (floppy->format.format_track)
+ {
+ heads = floppy_get_heads_per_disk(floppy);
+ tracks = floppy_get_tracks_per_disk(floppy);
+
+ for (h = 0; h < heads; h++)
+ {
+ for (t = 0; t < tracks; t++)
+ {
+ err = floppy->format.format_track(floppy, h, t, parameters);
+ if (err)
+ goto done;
+ }
+ }
+ }
+
+ /* call the post_format function, if present */
+ if (floppy->format.post_format)
+ {
+ err = floppy->format.post_format(floppy, parameters);
+ if (err)
+ goto done;
+ }
+
+ floppy->floppy_option = format;
+ err = FLOPPY_ERROR_SUCCESS;
+
+done:
+ if (err && floppy)
+ {
+ floppy_close_internal(floppy, FALSE);
+ floppy = NULL;
+ }
+
+ if (outfloppy)
+ *outfloppy = floppy;
+ else if (floppy)
+ floppy_close_internal(floppy, FALSE);
+
+ if (alloc_resolution)
+ option_resolution_close(alloc_resolution);
+ return err;
+}
+
+
+
+static void floppy_close_internal(floppy_image *floppy, int close_file)
+{
+ if (floppy) {
+ floppy_track_unload(floppy);
+ if (close_file)
+ io_generic_close(&floppy->io);
+ if (floppy->loaded_track_data)
+ free(floppy->loaded_track_data);
+ pool_free_lib(floppy->tags);
+
+ free(floppy);
+ }
+}
+
+
+
+void floppy_close(floppy_image *floppy)
+{
+ floppy_close_internal(floppy, TRUE);
+}
+
+
+
+/*********************************************************************
+ functions useful in format constructors
+*********************************************************************/
+
+struct FloppyCallbacks *floppy_callbacks(floppy_image *floppy)
+{
+ assert(floppy);
+ return &floppy->format;
+}
+
+
+
+void *floppy_tag(floppy_image *floppy)
+{
+ assert(floppy);
+ return floppy->tag_data;
+}
+
+
+
+void *floppy_create_tag(floppy_image *floppy, size_t tagsize)
+{
+ floppy->tag_data = pool_malloc_lib(floppy->tags,tagsize);
+ return floppy->tag_data;
+}
+
+
+
+UINT8 floppy_get_filler(floppy_image *floppy)
+{
+ return floppy->io.filler;
+}
+
+
+
+void floppy_set_filler(floppy_image *floppy, UINT8 filler)
+{
+ floppy->io.filler = filler;
+}
+
+
+
+/*********************************************************************
+ calls for accessing the raw disk image
+*********************************************************************/
+
+void floppy_image_read(floppy_image *floppy, void *buffer, UINT64 offset, size_t length)
+{
+ io_generic_read(&floppy->io, buffer, offset, length);
+}
+
+
+
+void floppy_image_write(floppy_image *floppy, const void *buffer, UINT64 offset, size_t length)
+{
+ io_generic_write(&floppy->io, buffer, offset, length);
+}
+
+
+
+void floppy_image_write_filler(floppy_image *floppy, UINT8 filler, UINT64 offset, size_t length)
+{
+ io_generic_write_filler(&floppy->io, filler, offset, length);
+}
+
+
+
+UINT64 floppy_image_size(floppy_image *floppy)
+{
+ return io_generic_size(&floppy->io);
+}
+
+
+
+/*********************************************************************
+ calls for accessing disk image data
+*********************************************************************/
+
+static floperr_t floppy_readwrite_sector(floppy_image *floppy, int head, int track, int sector, int offset,
+ void *buffer, size_t buffer_len, int writing, int indexed, int ddam)
+{
+ floperr_t err;
+ const struct FloppyCallbacks *fmt;
+ size_t this_buffer_len;
+ UINT8 *alloc_buf = NULL;
+ UINT32 sector_length;
+ UINT8 *buffer_ptr = (UINT8 *)buffer;
+ floperr_t (*read_sector)(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen);
+ floperr_t (*write_sector)(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam);
+
+ fmt = floppy_callbacks(floppy);
+
+ /* choose proper calls for indexed vs non-indexed */
+ if (indexed)
+ {
+ read_sector = fmt->read_indexed_sector;
+ write_sector = fmt->write_indexed_sector;
+ if (!fmt->get_indexed_sector_info)
+ {
+ err = FLOPPY_ERROR_UNSUPPORTED;
+ goto done;
+ }
+ }
+ else
+ {
+ read_sector = fmt->read_sector;
+ write_sector = fmt->write_sector;
+ if (!fmt->get_sector_length)
+ {
+ err = FLOPPY_ERROR_UNSUPPORTED;
+ goto done;
+ }
+ }
+
+ /* check to make sure that the operation is supported */
+ if (!read_sector || (writing && !write_sector))
+ {
+ err = FLOPPY_ERROR_UNSUPPORTED;
+ goto done;
+ }
+
+ /* main loop */
+ while(buffer_len > 0)
+ {
+ /* find out the size of this sector */
+ if (indexed)
+ err = fmt->get_indexed_sector_info(floppy, head, track, sector, NULL, NULL, NULL, &sector_length, NULL);
+ else
+ err = fmt->get_sector_length(floppy, head, track, sector, &sector_length);
+ if (err)
+ goto done;
+
+ /* do we even do anything with this sector? */
+ if (offset < sector_length)
+ {
+ /* ok we will be doing something */
+ if ((offset > 0) || (buffer_len < sector_length))
+ {
+ /* we will be doing an partial read/write; in other words we
+ * will not be reading/writing a full sector */
+ if (alloc_buf) free(alloc_buf);
+ alloc_buf = (UINT8*)malloc(sector_length);
+ if (!alloc_buf)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* read the sector (we need to do this even when writing */
+ err = read_sector(floppy, head, track, sector, alloc_buf, sector_length);
+ if (err)
+ goto done;
+
+ this_buffer_len = MIN(buffer_len, sector_length - offset);
+
+ if (writing)
+ {
+ memcpy(alloc_buf + offset, buffer_ptr, this_buffer_len);
+
+ err = write_sector(floppy, head, track, sector, alloc_buf, sector_length, ddam);
+ if (err)
+ goto done;
+ }
+ else
+ {
+ memcpy(buffer_ptr, alloc_buf + offset, this_buffer_len);
+ }
+ offset += this_buffer_len;
+ offset %= sector_length;
+ }
+ else
+ {
+ this_buffer_len = sector_length;
+
+ if (writing)
+ err = write_sector(floppy, head, track, sector, buffer_ptr, sector_length, ddam);
+ else
+ err = read_sector(floppy, head, track, sector, buffer_ptr, sector_length);
+ if (err)
+ goto done;
+ }
+ }
+ else
+ {
+ /* skip this sector */
+ offset -= sector_length;
+ this_buffer_len = 0;
+ }
+
+ buffer_ptr += this_buffer_len;
+ buffer_len -= this_buffer_len;
+ sector++;
+ }
+
+ err = FLOPPY_ERROR_SUCCESS;
+
+done:
+ if (alloc_buf)
+ free(alloc_buf);
+ return err;
+}
+
+
+
+floperr_t floppy_read_sector(floppy_image *floppy, int head, int track, int sector, int offset, void *buffer, size_t buffer_len)
+{
+ return floppy_readwrite_sector(floppy, head, track, sector, offset, buffer, buffer_len, FALSE, FALSE, 0);
+}
+
+
+
+floperr_t floppy_write_sector(floppy_image *floppy, int head, int track, int sector, int offset, const void *buffer, size_t buffer_len, int ddam)
+{
+ return floppy_readwrite_sector(floppy, head, track, sector, offset, (void *) buffer, buffer_len, TRUE, FALSE, ddam);
+}
+
+
+
+floperr_t floppy_read_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int offset, void *buffer, size_t buffer_len)
+{
+ return floppy_readwrite_sector(floppy, head, track, sector_index, offset, buffer, buffer_len, FALSE, TRUE, 0);
+}
+
+
+
+floperr_t floppy_write_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int offset, const void *buffer, size_t buffer_len, int ddam)
+{
+ return floppy_readwrite_sector(floppy, head, track, sector_index, offset, (void *) buffer, buffer_len, TRUE, TRUE, ddam);
+}
+
+
+static floperr_t floppy_get_track_data_offset(floppy_image *floppy, int head, int track, UINT64 *offset)
+{
+ floperr_t err;
+ const struct FloppyCallbacks *callbacks;
+
+ *offset = 0;
+ callbacks = floppy_callbacks(floppy);
+ if (callbacks->get_track_data_offset)
+ {
+ err = callbacks->get_track_data_offset(floppy, head, track, offset);
+ if (err)
+ return err;
+ }
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+static floperr_t floppy_read_track_offset(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buffer_len)
+{
+ floperr_t err;
+ const struct FloppyCallbacks *format;
+
+ format = floppy_callbacks(floppy);
+
+ if (!format->read_track)
+ return FLOPPY_ERROR_UNSUPPORTED;
+
+ err = floppy_track_unload(floppy);
+ if (err)
+ return err;
+
+ err = format->read_track(floppy, head, track, offset, buffer, buffer_len);
+ if (err)
+ return err;
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+floperr_t floppy_read_track(floppy_image *floppy, int head, int track, void *buffer, size_t buffer_len)
+{
+ return floppy_read_track_offset(floppy, head, track, 0, buffer, buffer_len);
+}
+
+
+
+floperr_t floppy_read_track_data(floppy_image *floppy, int head, int track, void *buffer, size_t buffer_len)
+{
+ floperr_t err;
+ UINT64 offset;
+
+ err = floppy_get_track_data_offset(floppy, head, track, &offset);
+ if (err)
+ return err;
+
+ return floppy_read_track_offset(floppy, head, track, offset, buffer, buffer_len);
+}
+
+
+
+static floperr_t floppy_write_track_offset(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buffer_len)
+{
+ floperr_t err;
+
+ /* track writing supported? */
+ if (!floppy_callbacks(floppy)->write_track)
+ return FLOPPY_ERROR_UNSUPPORTED;
+
+ /* read only? */
+ if (floppy->flags & FLOPPY_FLAGS_READONLY)
+ return FLOPPY_ERROR_READONLY;
+
+ err = floppy_track_unload(floppy);
+ if (err)
+ return err;
+
+ err = floppy_callbacks(floppy)->write_track(floppy, head, track, offset, buffer, buffer_len);
+ if (err)
+ return err;
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+floperr_t floppy_write_track(floppy_image *floppy, int head, int track, const void *buffer, size_t buffer_len)
+{
+ return floppy_write_track_offset(floppy, head, track, 0, buffer, buffer_len);
+}
+
+
+
+floperr_t floppy_write_track_data(floppy_image *floppy, int head, int track, const void *buffer, size_t buffer_len)
+{
+ floperr_t err;
+ UINT64 offset;
+
+ err = floppy_get_track_data_offset(floppy, head, track, &offset);
+ if (err)
+ return err;
+
+ return floppy_write_track_offset(floppy, head, track, offset, buffer, buffer_len);
+}
+
+
+
+floperr_t floppy_format_track(floppy_image *floppy, int head, int track, option_resolution *parameters)
+{
+ floperr_t err;
+ struct FloppyCallbacks *format;
+ option_resolution *alloc_resolution = NULL;
+ optreserr_t oerr;
+
+ /* supported? */
+ format = floppy_callbacks(floppy);
+ if (!format->format_track)
+ {
+ err = FLOPPY_ERROR_UNSUPPORTED;
+ goto done;
+ }
+
+ /* create a dummy resolution; if no parameters were specified */
+ if (!parameters)
+ {
+ alloc_resolution = option_resolution_create(floppy_option_guide, floppy->floppy_option->param_guidelines);
+ if (!alloc_resolution)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ parameters = alloc_resolution;
+ }
+
+ oerr = option_resolution_finish(parameters);
+ if (oerr)
+ {
+ err = option_to_floppy_error(oerr);
+ goto done;
+ }
+
+ err = format->format_track(floppy, head, track, parameters);
+ if (err)
+ goto done;
+
+done:
+ if (alloc_resolution)
+ option_resolution_close(alloc_resolution);
+ return err;
+}
+
+
+
+int floppy_get_tracks_per_disk(floppy_image *floppy)
+{
+ return floppy_callbacks(floppy)->get_tracks_per_disk(floppy);
+}
+
+
+
+int floppy_get_heads_per_disk(floppy_image *floppy)
+{
+ return floppy_callbacks(floppy)->get_heads_per_disk(floppy);
+}
+
+
+
+UINT32 floppy_get_track_size(floppy_image *floppy, int head, int track)
+{
+ const struct FloppyCallbacks *fmt;
+
+ fmt = floppy_callbacks(floppy);
+ if (!fmt->get_track_size)
+ return 0;
+
+ return fmt->get_track_size(floppy, head, track);
+}
+
+
+
+floperr_t floppy_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length)
+{
+ const struct FloppyCallbacks *fmt;
+
+ fmt = floppy_callbacks(floppy);
+ if (!fmt->get_sector_length)
+ return FLOPPY_ERROR_UNSUPPORTED;
+
+ return fmt->get_sector_length(floppy, head, track, sector, sector_length);
+}
+
+
+
+floperr_t floppy_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags)
+{
+ const struct FloppyCallbacks *fmt;
+
+ fmt = floppy_callbacks(floppy);
+ if (!fmt->get_indexed_sector_info)
+ return FLOPPY_ERROR_UNSUPPORTED;
+
+ return fmt->get_indexed_sector_info(floppy, head, track, sector_index, cylinder, side, sector, sector_length, flags);
+}
+
+
+
+floperr_t floppy_get_sector_count(floppy_image *floppy, int head, int track, int *sector_count)
+{
+ floperr_t err;
+ int sector_index = 0;
+
+ do
+ {
+ err = floppy_get_indexed_sector_info(floppy, head, track, sector_index, NULL, NULL, NULL, NULL, NULL);
+ if (!err)
+ sector_index++;
+ }
+ while(!err);
+
+ if (sector_index && (err == FLOPPY_ERROR_SEEKERROR))
+ err = FLOPPY_ERROR_SUCCESS;
+ if (sector_count)
+ *sector_count = err ? 0 : sector_index;
+ return err;
+}
+
+
+
+int floppy_is_read_only(floppy_image *floppy)
+{
+ return floppy->flags & FLOPPY_FLAGS_READONLY;
+}
+
+
+
+UINT8 floppy_random_byte(floppy_image *floppy)
+{
+ /* can't use mame_rand(); this might not be in the core */
+#ifdef rand
+#undef rand
+#endif
+ return rand();
+}
+
+
+
+/*********************************************************************
+ calls for track based IO
+*********************************************************************/
+
+floperr_t floppy_load_track(floppy_image *floppy, int head, int track, int dirtify, void **track_data, size_t *track_length)
+{
+ floperr_t err;
+ void *new_loaded_track_data;
+ UINT32 track_size;
+
+ /* have we already loaded this track? */
+ if (((floppy->loaded_track_status & TRACK_LOADED) == 0) || (head != floppy->loaded_track_head) || (track != floppy->loaded_track_index))
+ {
+ err = floppy_track_unload(floppy);
+ if (err)
+ goto error;
+
+ track_size = floppy_callbacks(floppy)->get_track_size(floppy, head, track);
+
+ if (floppy->loaded_track_data) free(floppy->loaded_track_data);
+ new_loaded_track_data = malloc(track_size);
+ if (!new_loaded_track_data)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto error;
+ }
+
+ floppy->loaded_track_data = new_loaded_track_data;
+ floppy->loaded_track_size = track_size;
+ floppy->loaded_track_head = head;
+ floppy->loaded_track_index = track;
+
+ err = floppy_callbacks(floppy)->read_track(floppy, floppy->loaded_track_head, floppy->loaded_track_index, 0, floppy->loaded_track_data, floppy->loaded_track_size);
+ if (err)
+ goto error;
+
+ floppy->loaded_track_status |= TRACK_LOADED | (dirtify ? TRACK_DIRTY : 0);
+ }
+ else
+ floppy->loaded_track_status |= (dirtify ? TRACK_DIRTY : 0);
+
+ if (track_data)
+ *track_data = floppy->loaded_track_data;
+ if (track_length)
+ *track_length = floppy->loaded_track_size;
+ return FLOPPY_ERROR_SUCCESS;
+
+error:
+ if (track_data)
+ *track_data = NULL;
+ if (track_length)
+ *track_length = 0;
+ return err;
+}
+
+
+
+static floperr_t floppy_track_unload(floppy_image *floppy)
+{
+ int err;
+ if (floppy->loaded_track_status & TRACK_DIRTY)
+ {
+ err = floppy_callbacks(floppy)->write_track(floppy, floppy->loaded_track_head, floppy->loaded_track_index, 0, floppy->loaded_track_data, floppy->loaded_track_size);
+ if (err)
+ return (floperr_t)err;
+ }
+
+ floppy->loaded_track_status &= ~(TRACK_LOADED | TRACK_DIRTY);
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+/*********************************************************************
+ accessors for meta information about the image
+*********************************************************************/
+
+const char *floppy_format_description(floppy_image *floppy)
+{
+ return floppy->floppy_option->description;
+}
+
+
+
+/*********************************************************************
+ misc calls
+*********************************************************************/
+
+const char *floppy_error(floperr_t err)
+{
+ static const char *const error_messages[] =
+ {
+ "The operation completed successfully",
+ "Fatal internal error",
+ "This operation is unsupported",
+ "Out of memory",
+ "Seek error",
+ "Invalid image",
+ "Attempted to write to read only image",
+ "No space left on image",
+ "Parameter out of range",
+ "Required parameter not specified"
+ };
+
+ if ((err < 0) || (err >= ARRAY_LENGTH(error_messages)))
+ return NULL;
+ return error_messages[err];
+}
+
+
+FLOPPY_OPTIONS_START(default)
+FLOPPY_OPTIONS_END
diff --git a/src/emu/imagedev/flopimg.h b/src/emu/imagedev/flopimg.h
new file mode 100644
index 00000000000..22dbfd41746
--- /dev/null
+++ b/src/emu/imagedev/flopimg.h
@@ -0,0 +1,204 @@
+/*********************************************************************
+
+ flopimg.h
+
+ Floppy disk image abstraction code
+
+*********************************************************************/
+
+#ifndef FLOPIMG_H
+#define FLOPIMG_H
+
+#include "emu.h"
+#include "opresolv.h"
+
+
+/***************************************************************************
+
+ Constants
+
+***************************************************************************/
+
+#define FLOPPY_FLAGS_READWRITE 0
+#define FLOPPY_FLAGS_READONLY 1
+
+
+
+/***************************************************************************
+
+ Type definitions
+
+***************************************************************************/
+
+typedef enum
+{
+ FLOPPY_ERROR_SUCCESS, /* no error */
+ FLOPPY_ERROR_INTERNAL, /* fatal internal error */
+ FLOPPY_ERROR_UNSUPPORTED, /* this operation is unsupported */
+ FLOPPY_ERROR_OUTOFMEMORY, /* ran out of memory */
+ FLOPPY_ERROR_SEEKERROR, /* attempted to seek to nonexistant location */
+ FLOPPY_ERROR_INVALIDIMAGE, /* this image in invalid */
+ FLOPPY_ERROR_READONLY, /* attempt to write to read-only image */
+ FLOPPY_ERROR_NOSPACE,
+ FLOPPY_ERROR_PARAMOUTOFRANGE,
+ FLOPPY_ERROR_PARAMNOTSPECIFIED
+}
+floperr_t;
+
+typedef struct _floppy_image floppy_image;
+
+struct FloppyCallbacks
+{
+ floperr_t (*read_sector)(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen);
+ floperr_t (*write_sector)(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam);
+ floperr_t (*read_indexed_sector)(floppy_image *floppy, int head, int track, int sector_index, void *buffer, size_t buflen);
+ floperr_t (*write_indexed_sector)(floppy_image *floppy, int head, int track, int sector_index, const void *buffer, size_t buflen, int ddam);
+ floperr_t (*read_track)(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen);
+ floperr_t (*write_track)(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen);
+ floperr_t (*format_track)(floppy_image *floppy, int head, int track, option_resolution *params);
+ floperr_t (*post_format)(floppy_image *floppy, option_resolution *params);
+ int (*get_heads_per_disk)(floppy_image *floppy);
+ int (*get_tracks_per_disk)(floppy_image *floppy);
+ int (*get_sectors_per_track)(floppy_image *floppy, int head, int track);
+ UINT32 (*get_track_size)(floppy_image *floppy, int head, int track);
+ floperr_t (*get_sector_length)(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length);
+ floperr_t (*get_indexed_sector_info)(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags);
+ floperr_t (*get_track_data_offset)(floppy_image *floppy, int head, int track, UINT64 *offset);
+};
+
+
+
+struct FloppyFormat
+{
+ const char *name;
+ const char *extensions;
+ const char *description;
+ floperr_t (*identify)(floppy_image *floppy, const struct FloppyFormat *format, int *vote);
+ floperr_t (*construct)(floppy_image *floppy, const struct FloppyFormat *format, option_resolution *params);
+ const char *param_guidelines;
+};
+
+#define FLOPPY_IDENTIFY(name) floperr_t name(floppy_image *floppy, const struct FloppyFormat *format, int *vote)
+#define FLOPPY_CONSTRUCT(name) floperr_t name(floppy_image *floppy, const struct FloppyFormat *format, option_resolution *params)
+
+FLOPPY_IDENTIFY(td0_dsk_identify);
+FLOPPY_CONSTRUCT(td0_dsk_construct);
+
+FLOPPY_IDENTIFY(imd_dsk_identify);
+FLOPPY_CONSTRUCT(imd_dsk_construct);
+
+FLOPPY_IDENTIFY(cqm_dsk_identify);
+FLOPPY_CONSTRUCT(cqm_dsk_construct);
+
+FLOPPY_IDENTIFY(dsk_dsk_identify);
+FLOPPY_CONSTRUCT(dsk_dsk_construct);
+
+FLOPPY_IDENTIFY(d88_dsk_identify);
+FLOPPY_CONSTRUCT(d88_dsk_construct);
+
+FLOPPY_IDENTIFY(fdi_dsk_identify);
+FLOPPY_CONSTRUCT(fdi_dsk_construct);
+
+#define FLOPPY_OPTIONS_NAME(name) floppyoptions_##name
+
+#define FLOPPY_OPTIONS_START(name) \
+ const struct FloppyFormat floppyoptions_##name[] = \
+ { \
+
+#define FLOPPY_OPTIONS_END0 \
+ { NULL } \
+ };
+
+#define FLOPPY_OPTIONS_EXTERN(name) \
+ extern const struct FloppyFormat floppyoptions_##name[] \
+
+#define FLOPPY_OPTION(name, extensions_, description_, identify_, construct_, ranges_)\
+ { #name, extensions_, description_, identify_, construct_, ranges_ }, \
+
+#define FLOPPY_OPTIONS_END \
+ FLOPPY_OPTION( fdi, "fdi", "Formatted Disk Image", fdi_dsk_identify, fdi_dsk_construct, NULL) \
+ FLOPPY_OPTION( td0, "td0", "Teledisk floppy disk image", td0_dsk_identify, td0_dsk_construct, NULL) \
+ FLOPPY_OPTION( imd, "imd", "IMD floppy disk image", imd_dsk_identify, imd_dsk_construct, NULL) \
+ FLOPPY_OPTION( cqm, "cqm,dsk", "CopyQM floppy disk image", cqm_dsk_identify, cqm_dsk_construct, NULL) \
+ FLOPPY_OPTION( dsk, "dsk", "DSK floppy disk image", dsk_dsk_identify, dsk_dsk_construct, NULL) \
+ FLOPPY_OPTION( d88, "d77,d88,1dd", "D88 Floppy Disk image", d88_dsk_identify, d88_dsk_construct, NULL) \
+ FLOPPY_OPTIONS_END0
+
+FLOPPY_OPTIONS_EXTERN(default);
+
+#define PARAM_END '\0'
+#define PARAM_HEADS 'H'
+#define PARAM_TRACKS 'T'
+#define PARAM_SECTORS 'S'
+#define PARAM_SECTOR_LENGTH 'L'
+#define PARAM_INTERLEAVE 'I'
+#define PARAM_FIRST_SECTOR_ID 'F'
+
+#define HEADS(range) "H" #range
+#define TRACKS(range) "T" #range
+#define SECTORS(range) "S" #range
+#define SECTOR_LENGTH(range) "L" #range
+#define INTERLEAVE(range) "I" #range
+#define FIRST_SECTOR_ID(range) "F" #range
+
+
+/***************************************************************************
+
+ Prototypes
+
+***************************************************************************/
+
+OPTION_GUIDE_EXTERN(floppy_option_guide);
+
+/* opening, closing and creating of floppy images */
+floperr_t floppy_open(void *fp, const struct io_procs *procs, const char *extension, const struct FloppyFormat *format, int flags, floppy_image **outfloppy);
+floperr_t floppy_open_choices(void *fp, const struct io_procs *procs, const char *extension, const struct FloppyFormat *formats, int flags, floppy_image **outfloppy);
+floperr_t floppy_create(void *fp, const struct io_procs *procs, const struct FloppyFormat *format, option_resolution *parameters, floppy_image **outfloppy);
+void floppy_close(floppy_image *floppy);
+
+/* useful for identifying a floppy image */
+floperr_t floppy_identify(void *fp, const struct io_procs *procs, const char *extension,
+ const struct FloppyFormat *formats, int *identified_format);
+
+/* functions useful within format constructors */
+void *floppy_tag(floppy_image *floppy);
+void *floppy_create_tag(floppy_image *floppy, size_t tagsize);
+struct FloppyCallbacks *floppy_callbacks(floppy_image *floppy);
+UINT8 floppy_get_filler(floppy_image *floppy);
+void floppy_set_filler(floppy_image *floppy, UINT8 filler);
+
+/* calls for accessing disk image data */
+floperr_t floppy_read_sector(floppy_image *floppy, int head, int track, int sector, int offset, void *buffer, size_t buffer_len);
+floperr_t floppy_write_sector(floppy_image *floppy, int head, int track, int sector, int offset, const void *buffer, size_t buffer_len, int ddam);
+floperr_t floppy_read_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int offset, void *buffer, size_t buffer_len);
+floperr_t floppy_write_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int offset, const void *buffer, size_t buffer_len, int ddam);
+floperr_t floppy_read_track(floppy_image *floppy, int head, int track, void *buffer, size_t buffer_len);
+floperr_t floppy_write_track(floppy_image *floppy, int head, int track, const void *buffer, size_t buffer_len);
+floperr_t floppy_read_track_data(floppy_image *floppy, int head, int track, void *buffer, size_t buffer_len);
+floperr_t floppy_write_track_data(floppy_image *floppy, int head, int track, const void *buffer, size_t buffer_len);
+floperr_t floppy_format_track(floppy_image *floppy, int head, int track, option_resolution *params);
+int floppy_get_tracks_per_disk(floppy_image *floppy);
+int floppy_get_heads_per_disk(floppy_image *floppy);
+UINT32 floppy_get_track_size(floppy_image *floppy, int head, int track);
+floperr_t floppy_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length);
+floperr_t floppy_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags);
+floperr_t floppy_get_sector_count(floppy_image *floppy, int head, int track, int *sector_count);
+floperr_t floppy_load_track(floppy_image *floppy, int head, int track, int dirtify, void **track_data, size_t *track_length);
+int floppy_is_read_only(floppy_image *floppy);
+UINT8 floppy_random_byte(floppy_image *floppy);
+
+/* accessors for meta information about the image */
+const char *floppy_format_description(floppy_image *floppy);
+
+/* calls for accessing the raw disk image */
+void floppy_image_read(floppy_image *floppy, void *buffer, UINT64 offset, size_t length);
+void floppy_image_write(floppy_image *floppy, const void *buffer, UINT64 offset, size_t length);
+void floppy_image_write_filler(floppy_image *floppy, UINT8 filler, UINT64 offset, size_t length);
+UINT64 floppy_image_size(floppy_image *floppy);
+
+/* misc */
+const char *floppy_error(floperr_t err);
+
+
+#endif /* FLOPIMG_H */
+
diff --git a/src/emu/imagedev/harddriv.c b/src/emu/imagedev/harddriv.c
new file mode 100644
index 00000000000..db18d547729
--- /dev/null
+++ b/src/emu/imagedev/harddriv.c
@@ -0,0 +1,404 @@
+/*********************************************************************
+
+ Code to interface the MESS image code with MAME's harddisk core.
+
+ We do not support diff files as it will involve some changes in
+ the MESS image code. Additionally, the need for diff files comes
+ from MAME's need for "cannonical" hard drive images.
+
+ Raphael Nabet 2003
+
+ Update: 23-Feb-2004 - Unlike floppy disks, for which we support
+ myriad formats on many systems, it is my intention for MESS to
+ standardize on the CHD file format for hard drives so I made a few
+ changes to support this
+
+*********************************************************************/
+
+#include "emu.h"
+#include "harddisk.h"
+#include "harddriv.h"
+
+
+static const char *const error_strings[] =
+{
+ "no error",
+ "no drive interface",
+ "out of memory",
+ "invalid file",
+ "invalid parameter",
+ "invalid data",
+ "file not found",
+ "requires parent",
+ "file not writeable",
+ "read error",
+ "write error",
+ "codec error",
+ "invalid parent",
+ "hunk out of range",
+ "decompression error",
+ "compression error",
+ "can't create file",
+ "can't verify file"
+ "operation not supported",
+ "can't find metadata",
+ "invalid metadata size",
+ "unsupported CHD version"
+};
+
+static const char *chd_get_error_string(int chderr)
+{
+ if ((chderr < 0 ) || (chderr >= ARRAY_LENGTH(error_strings)))
+ return NULL;
+ return error_strings[chderr];
+}
+
+
+
+static OPTION_GUIDE_START(mess_hd_option_guide)
+ OPTION_INT('C', "cylinders", "Cylinders")
+ OPTION_INT('H', "heads", "Heads")
+ OPTION_INT('S', "sectors", "Sectors")
+ OPTION_INT('L', "sectorlength", "Sector Bytes")
+ OPTION_INT('K', "hunksize", "Hunk Bytes")
+OPTION_GUIDE_END
+
+static const char *mess_hd_option_spec =
+ "C1-[512]-1024;H1/2/[4]/8;S1-[16]-64;L128/256/[512]/1024;K512/1024/2048/[4096]";
+
+
+typedef struct _dev_harddisk_t dev_harddisk_t;
+struct _dev_harddisk_t
+{
+ const struct harddisk_callback_config *config;
+ chd_file *chd;
+ hard_disk_file *hard_disk_handle;
+};
+
+
+INLINE dev_harddisk_t *get_safe_token(device_t *device)
+{
+ assert( device != NULL );
+ assert( ( device->type() == HARDDISK ) ||
+ ( device->type() == IDE_HARDDISK ) );
+ return (dev_harddisk_t *) downcast<legacy_device_base *>(device)->token();
+}
+
+
+/*************************************
+ *
+ * DEVICE_IMAGE_LOAD(mess_hd)
+ * DEVICE_IMAGE_CREATE(mess_hd)
+ *
+ * Device load and create
+ *
+ *************************************/
+
+static int internal_load_mess_hd(device_image_interface &image, const char *metadata)
+{
+ dev_harddisk_t *harddisk = get_safe_token( &image.device() );
+ chd_error err = (chd_error)0;
+ int is_writeable;
+
+ /* open the CHD file */
+ do
+ {
+ is_writeable = image.is_writable();
+ harddisk->chd = NULL;
+ err = chd_open_file(image.image_core_file(), is_writeable ? CHD_OPEN_READWRITE : CHD_OPEN_READ, NULL, &harddisk->chd);
+
+ /* special case; if we get CHDERR_FILE_NOT_WRITEABLE, make the
+ * image read only and repeat */
+ if (err == CHDERR_FILE_NOT_WRITEABLE)
+ image.make_readonly();
+ }
+ while(!harddisk->chd && is_writeable && (err == CHDERR_FILE_NOT_WRITEABLE));
+ if (!harddisk->chd)
+ goto done;
+
+ /* if we created the image and hence, have metadata to set, set the metadata */
+ if (metadata)
+ {
+ err = chd_set_metadata(harddisk->chd, HARD_DISK_METADATA_TAG, 0, metadata, strlen(metadata) + 1, 0);
+ if (err != CHDERR_NONE)
+ goto done;
+ }
+
+ /* open the hard disk file */
+ harddisk->hard_disk_handle = hard_disk_open(harddisk->chd);
+ if (!harddisk->hard_disk_handle)
+ goto done;
+
+done:
+ if (err)
+ {
+ /* if we had an error, close out the CHD */
+ if (harddisk->chd != NULL)
+ {
+ chd_close(harddisk->chd);
+ harddisk->chd = NULL;
+ }
+
+ image.seterror(IMAGE_ERROR_UNSPECIFIED, chd_get_error_string(err));
+ }
+ return err ? IMAGE_INIT_FAIL : IMAGE_INIT_PASS;
+}
+
+
+
+static DEVICE_IMAGE_LOAD( mess_hd )
+{
+ dev_harddisk_t *harddisk = get_safe_token( image );
+ int our_result;
+
+ our_result = internal_load_mess_hd(image, NULL);
+
+ /* Check if there is an image_load callback defined */
+ if ( harddisk->config && harddisk->config->device_image_load )
+ {
+ /* Let the override do some additional work/checks */
+ our_result = harddisk->config->device_image_load( image );
+ }
+ return our_result;
+}
+
+
+static DEVICE_IMAGE_CREATE( mess_hd )
+{
+ int err;
+ char metadata[256];
+ UINT32 sectorsize, hunksize;
+ UINT32 cylinders, heads, sectors, totalsectors;
+
+ cylinders = option_resolution_lookup_int(create_args, 'C');
+ heads = option_resolution_lookup_int(create_args, 'H');
+ sectors = option_resolution_lookup_int(create_args, 'S');
+ sectorsize = option_resolution_lookup_int(create_args, 'L');
+ hunksize = option_resolution_lookup_int(create_args, 'K');
+
+ totalsectors = cylinders * heads * sectors;
+
+ /* create the CHD file */
+ err = chd_create_file(image.image_core_file(), (UINT64)totalsectors * (UINT64)sectorsize, hunksize, CHDCOMPRESSION_NONE, NULL);
+ if (err != CHDERR_NONE)
+ goto error;
+
+ sprintf(metadata, HARD_DISK_METADATA_FORMAT, cylinders, heads, sectors, sectorsize);
+ return internal_load_mess_hd(image, metadata);
+
+error:
+ return IMAGE_INIT_FAIL;
+}
+
+
+
+/*************************************
+ *
+ * DEVICE_IMAGE_UNLOAD(mess_hd)
+ *
+ * Device unload
+ *
+ *************************************/
+
+static DEVICE_IMAGE_UNLOAD( mess_hd )
+{
+ dev_harddisk_t *harddisk = get_safe_token( image );
+
+ /* Check if there is an image_unload callback defined */
+ if ( harddisk->config && harddisk->config->device_image_unload )
+ {
+ harddisk->config->device_image_unload( image );
+ }
+
+ if (harddisk->hard_disk_handle != NULL)
+ {
+ hard_disk_close(harddisk->hard_disk_handle);
+ harddisk->hard_disk_handle = NULL;
+ }
+
+ if (harddisk->chd != NULL)
+ {
+ chd_close(harddisk->chd);
+ harddisk->chd = NULL;
+ }
+}
+
+
+/*************************************
+ *
+ * Get the MESS/MAME hard disk handle (from the src/harddisk.c core)
+ * after an image has been opened with the mess_hd core
+ *
+ *************************************/
+
+hard_disk_file *mess_hd_get_hard_disk_file(device_t *device)
+{
+ dev_harddisk_t *harddisk = get_safe_token( device );
+
+ return harddisk->hard_disk_handle;
+}
+
+
+/*************************************
+ *
+ * Get the MESS/MAME CHD file (from the src/chd.c core)
+ * after an image has been opened with the mess_hd core
+ *
+ *************************************/
+
+chd_file *mess_hd_get_chd_file(device_t *device)
+{
+ chd_file *result = NULL;
+ hard_disk_file *hd_file;
+
+ if (device)
+ {
+ hd_file = mess_hd_get_hard_disk_file(device);
+ if (hd_file)
+ result = hard_disk_get_chd(hd_file);
+ }
+ return result;
+}
+
+
+/*-------------------------------------------------
+ DEVICE_START(mess_hd)
+-------------------------------------------------*/
+
+static DEVICE_START(mess_hd)
+{
+ dev_harddisk_t *harddisk = get_safe_token( device );
+
+ harddisk->config = (const harddisk_callback_config*)device->baseconfig().static_config();
+ harddisk->chd = NULL;
+ harddisk->hard_disk_handle = NULL;
+}
+
+
+/*-------------------------------------------------
+ DEVICE_GET_INFO(mess_hd)
+-------------------------------------------------*/
+
+DEVICE_GET_INFO(mess_hd)
+{
+ switch( state )
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(dev_harddisk_t); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+ case DEVINFO_INT_IMAGE_TYPE: info->i = IO_HARDDISK; break;
+ case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_CREATABLE: info->i = 0; break;
+ case DEVINFO_INT_IMAGE_CREATE_OPTCOUNT: info->i = 1; break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(mess_hd); break;
+ case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(mess_hd); break;
+ case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(mess_hd); break;
+ case DEVINFO_FCT_IMAGE_CREATE: info->f = (genf *) DEVICE_IMAGE_CREATE_NAME(mess_hd); break;
+ case DEVINFO_PTR_IMAGE_CREATE_OPTGUIDE: info->p = (void *) mess_hd_option_guide; break;
+ case DEVINFO_PTR_IMAGE_CREATE_OPTSPEC+0: info->p = (void *) mess_hd_option_spec; break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Harddisk"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Harddisk"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, "chd,hd"); break;
+ case DEVINFO_STR_IMAGE_CREATE_OPTNAME+0: strcpy(info->s, "chd"); break;
+ case DEVINFO_STR_IMAGE_CREATE_OPTDESC+0: strcpy(info->s, "MAME/MESS CHD Hard drive"); break;
+ case DEVINFO_STR_IMAGE_CREATE_OPTEXTS+0: strcpy(info->s, "chd,hd"); break;
+ }
+}
+
+
+static DEVICE_START(mess_ide)
+{
+ /* old code from idedrive.c */
+#ifdef UNUSED_FUNCTION
+ int which_bus, which_address;
+ struct ide_interface *intf;
+ device_start_func parent_init;
+
+ /* get the basics */
+ ide_get_params(device, &which_bus, &which_address, &intf, &parent_init, NULL, NULL);
+
+ /* call the parent init function */
+ parent_init(device);
+
+ /* configure IDE */
+ /* FIXME IDE */
+ /* ide_controller_init_custom(which_bus, intf, NULL); */
+#endif
+}
+
+
+static DEVICE_IMAGE_LOAD(mess_ide)
+{
+ /* old code from idedrive.c */
+#ifdef UNUSED_FUNCTION
+ int result, which_bus, which_address;
+ struct ide_interface *intf;
+ device_image_load_func parent_load;
+
+ /* get the basics */
+ ide_get_params(image, &which_bus, &which_address, &intf, NULL, &parent_load, NULL);
+
+ /* call the parent load function */
+ result = parent_load(image);
+ if (result != IMAGE_INIT_PASS)
+ return result;
+
+ /* configure IDE */
+ /* FIXME IDE */
+ /* ide_controller_init_custom(which_bus, intf, mess_hd_get_chd_file(image)); */
+ /* ide_controller_reset(which_bus); */
+ return IMAGE_INIT_PASS;
+#endif
+ return device_load_mess_hd( image );
+}
+
+
+static DEVICE_IMAGE_UNLOAD(mess_ide)
+{
+ /* old code from idedrive.c */
+#ifdef UNUSED_FUNCTION
+ int which_bus, which_address;
+ struct ide_interface *intf;
+ device_image_unload_func parent_unload;
+
+ /* get the basics */
+ ide_get_params(image, &which_bus, &which_address, &intf, NULL, NULL, &parent_unload);
+
+ /* call the parent unload function */
+ parent_unload(image);
+
+ /* configure IDE */
+ /* FIXME IDE */
+ /* ide_controller_init_custom(which_bus, intf, NULL); */
+ /* ide_controller_reset(which_bus); */
+#endif
+ device_unload_mess_hd( image );
+}
+
+
+DEVICE_GET_INFO(mess_ide)
+{
+ switch( state )
+ {
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(mess_ide); break;
+ case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(mess_ide); break;
+ case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(mess_ide); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "IDE harddisk"); break;
+ case DEVINFO_STR_IMAGE_INSTANCE_NAME: strcpy(info->s, "ideharddrive"); break;
+ case DEVINFO_STR_IMAGE_BRIEF_INSTANCE_NAME: strcpy(info->s, "idehd"); break;
+
+ default: DEVICE_GET_INFO_CALL( mess_hd ); break;
+ }
+}
+
+DEFINE_LEGACY_IMAGE_DEVICE(HARDDISK, mess_hd);
+DEFINE_LEGACY_IMAGE_DEVICE(IDE_HARDDISK, mess_ide);
diff --git a/src/emu/imagedev/harddriv.h b/src/emu/imagedev/harddriv.h
new file mode 100644
index 00000000000..05dc1edde0d
--- /dev/null
+++ b/src/emu/imagedev/harddriv.h
@@ -0,0 +1,34 @@
+/*********************************************************************
+
+ harddriv.h
+
+ MESS interface to the MAME CHD code
+
+*********************************************************************/
+
+#ifndef MESS_HD_H
+#define MESS_HD_H
+
+#include "image.h"
+#include "harddisk.h"
+
+DECLARE_LEGACY_IMAGE_DEVICE(HARDDISK, mess_hd);
+DECLARE_LEGACY_IMAGE_DEVICE(IDE_HARDDISK, mess_ide);
+
+#define MCFG_HARDDISK_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, HARDDISK, 0) \
+
+#define MCFG_IDE_HARDDISK_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, IDE_HARDDISK, 0) \
+
+hard_disk_file *mess_hd_get_hard_disk_file(device_t *device);
+chd_file *mess_hd_get_chd_file(device_t *device);
+
+struct harddisk_callback_config
+{
+ device_image_load_func device_image_load;
+ device_image_unload_func device_image_unload;
+};
+
+
+#endif /* MESS_HD_H */
diff --git a/src/emu/imagedev/multcart.c b/src/emu/imagedev/multcart.c
new file mode 100644
index 00000000000..1c0eb9f3d41
--- /dev/null
+++ b/src/emu/imagedev/multcart.c
@@ -0,0 +1,607 @@
+/*********************************************************************
+
+ multcart.c
+
+ Multi-cartridge handling code
+
+*********************************************************************/
+
+#include "multcart.h"
+#include "pool.h"
+#include "unzip.h"
+#include "corestr.h"
+#include "xmlfile.h"
+#include "emu.h"
+#include "hash.h"
+#include "machine/ram.h"
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+struct _multicart_private
+{
+ object_pool * pool;
+ zip_file * zip;
+};
+
+
+typedef struct _multicart_load_state multicart_load_state;
+struct _multicart_load_state
+{
+ multicart_t * multicart;
+ zip_file * zip;
+ xml_data_node * layout_xml;
+ xml_data_node * resources_node;
+ xml_data_node * pcb_node;
+ multicart_resource * resources;
+ multicart_socket * sockets;
+};
+
+static const char error_text[14][30] =
+{
+ "no error",
+ "not a multicart",
+ "module definition corrupt",
+ "out of memory",
+ "xml format error",
+ "invalid file reference",
+ "zip file error",
+ "missing ram length",
+ "invalid ram specification",
+ "unknown resource type",
+ "invalid resource reference",
+ "invalid file format",
+ "missing layout",
+ "no pcb or resource found"
+};
+
+const char *multicart_error_text(multicart_open_error error)
+{
+ return error_text[(int)error];
+}
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ find_file
+-------------------------------------------------*/
+
+static const zip_file_header *find_file(zip_file *zip, const char *filename)
+{
+ const zip_file_header *header;
+ for (header = zip_file_first_file(zip); header != NULL; header = zip_file_next_file(zip))
+ {
+ if (!core_stricmp(header->filename, filename))
+ return header;
+ }
+ return NULL;
+}
+
+static const zip_file_header *find_file_crc(zip_file *zip, const char *filename, UINT32 crc)
+{
+ const zip_file_header *header;
+ for (header = zip_file_first_file(zip); header != NULL; header = zip_file_next_file(zip))
+ {
+ // if the CRC and name both match, we're good
+ // if the CRC matches and the name doesn't, we're still good
+ if ((!core_stricmp(header->filename, filename)) && (header->crc == crc))
+ {
+ return header;
+ }
+ else if (header->crc == crc)
+ {
+ return header;
+ }
+ }
+ return NULL;
+}
+
+/*-------------------------------------------------
+ find_pcb_and_resource_nodes
+-------------------------------------------------*/
+
+static int find_pcb_and_resource_nodes(xml_data_node *layout_xml,
+ xml_data_node **pcb_node, xml_data_node **resource_node)
+{
+ xml_data_node *romset_node;
+ xml_data_node *configuration_node;
+
+ *pcb_node = NULL;
+ *resource_node = NULL;
+
+ romset_node = xml_get_sibling(layout_xml->child, "romset");
+ if (romset_node == NULL)
+ return FALSE;
+
+ configuration_node = xml_get_sibling(romset_node->child, "configuration");
+ if (configuration_node == NULL)
+ return FALSE;
+
+ *pcb_node = xml_get_sibling(configuration_node->child, "pcb");
+ if (*pcb_node == NULL)
+ return FALSE;
+
+ *resource_node = xml_get_sibling(romset_node->child, "resources");
+ if (*resource_node == NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ get_resource_type
+-------------------------------------------------*/
+
+static multicart_resource_type get_resource_type(const char *s)
+{
+ multicart_resource_type result;
+ if (!strcmp(s, "rom"))
+ result = MULTICART_RESOURCE_TYPE_ROM;
+ else if (!strcmp(s, "ram"))
+ result = MULTICART_RESOURCE_TYPE_RAM;
+ else
+ result = MULTICART_RESOURCE_TYPE_INVALID;
+ return result;
+}
+
+
+/*-------------------------------------------------
+ load_rom_resource
+-------------------------------------------------*/
+
+static multicart_open_error load_rom_resource(multicart_load_state *state, xml_data_node *resource_node,
+ multicart_resource *resource)
+{
+ const char *file, *crcstr, *sha1;
+ const zip_file_header *header;
+ zip_error ziperr;
+ UINT32 crc;
+
+ /* locate the 'file' attribute */
+ file = xml_get_attribute_string(resource_node, "file", NULL);
+ if (file == NULL)
+ return MCERR_XML_ERROR;
+
+ if (!(crcstr = xml_get_attribute_string(resource_node, "crc", NULL)))
+ {
+ /* locate the file in the ZIP file */
+ header = find_file(state->zip, file);
+ }
+ else /* CRC tag is present, use it */
+ {
+ crc = strtoul(crcstr, NULL, 16);
+ header = find_file_crc(state->zip, file, crc);
+ }
+ if (header == NULL)
+ return MCERR_INVALID_FILE_REF;
+ resource->length = header->uncompressed_length;
+
+ /* allocate bytes for this resource */
+ resource->ptr = pool_malloc_lib(state->multicart->data->pool, resource->length);
+ if (resource->ptr == NULL)
+ return MCERR_OUT_OF_MEMORY;
+
+ /* and decompress it */
+ ziperr = zip_file_decompress(state->zip, resource->ptr, resource->length);
+ if (ziperr != ZIPERR_NONE)
+ return MCERR_ZIP_ERROR;
+
+ /* check SHA1 now */
+ if ((sha1 = xml_get_attribute_string(resource_node, "sha1", NULL)))
+ {
+ char calc_sha[256];
+
+ memset(calc_sha, 0, sizeof(calc_sha));
+ hash_compute(&calc_sha[0], (const unsigned char*)resource->ptr, resource->length, HASH_SHA1);
+
+ if ((strncmp(sha1, &calc_sha[2], 20)))
+ {
+ return MCERR_INVALID_FILE_REF;
+ }
+ }
+
+ return MCERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ load_ram_resource
+-------------------------------------------------*/
+
+static multicart_open_error load_ram_resource(multicart_load_state *state, xml_data_node *resource_node,
+ multicart_resource *resource)
+{
+ const char *length_string;
+ const char *ram_type;
+ const char *ram_filename;
+
+ astring *ram_pathname;
+
+ /* locate the 'length' attribute */
+ length_string = xml_get_attribute_string(resource_node, "length", NULL);
+ if (length_string == NULL)
+ return MCERR_MISSING_RAM_LENGTH;
+
+ /* ...and parse it */
+ resource->length = ram_parse_string(length_string);
+ if (resource->length <= 0)
+ return MCERR_INVALID_RAM_SPEC;
+
+ /* allocate bytes for this resource */
+ resource->ptr = pool_malloc_lib(state->multicart->data->pool, resource->length);
+ if (resource->ptr == NULL)
+ return MCERR_OUT_OF_MEMORY;
+
+ /* Is this a persistent RAM resource? Then try to load it. */
+ ram_type = xml_get_attribute_string(resource_node, "type", NULL);
+ if (ram_type != NULL)
+ {
+ if (strcmp(ram_type, "persistent")==0)
+ {
+ astring tmp;
+
+ /* Get the file name. */
+ ram_filename = xml_get_attribute_string(resource_node, "file", NULL);
+ if (ram_filename==NULL)
+ return MCERR_XML_ERROR;
+
+ ram_pathname = astring_assemble_3(&tmp, state->multicart->gamedrv_name, PATH_SEPARATOR, ram_filename);
+
+ /* Save the file name so that we can write the contents on unloading.
+ If the RAM resource has no filename, we know that it was volatile only. */
+ resource->filename = pool_strdup_lib(state->multicart->data->pool, astring_c(ram_pathname));
+
+ if (resource->filename == NULL)
+ return MCERR_OUT_OF_MEMORY;
+
+ image_battery_load_by_name(resource->filename, resource->ptr, resource->length, 0x00);
+ }
+ /* else this type is volatile, in which case we just have
+ a memory expansion */
+ }
+ return MCERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ load_resource
+-------------------------------------------------*/
+
+static multicart_open_error load_resource(multicart_load_state *state, xml_data_node *resource_node,
+ multicart_resource_type resource_type)
+{
+ const char *id;
+ multicart_open_error err;
+ multicart_resource *resource;
+ multicart_resource **next_resource;
+
+ /* get the 'id' attribute; error if not present */
+ id = xml_get_attribute_string(resource_node, "id", NULL);
+ if (id == NULL)
+ return MCERR_XML_ERROR;
+
+ /* allocate memory for the resource */
+ resource = (multicart_resource *)pool_malloc_lib(state->multicart->data->pool, sizeof(*resource));
+ if (resource == NULL)
+ return MCERR_OUT_OF_MEMORY;
+ memset(resource, 0, sizeof(*resource));
+ resource->type = resource_type;
+
+ /* copy id */
+ resource->id = pool_strdup_lib(state->multicart->data->pool, id);
+ if (resource->id == NULL)
+ return MCERR_OUT_OF_MEMORY;
+
+ switch(resource->type)
+ {
+ case MULTICART_RESOURCE_TYPE_ROM:
+ err = load_rom_resource(state, resource_node, resource);
+ if (err != MCERR_NONE)
+ return err;
+ break;
+
+ case MULTICART_RESOURCE_TYPE_RAM:
+ err = load_ram_resource(state, resource_node, resource);
+ if (err != MCERR_NONE)
+ return err;
+ break;
+
+ default:
+ return MCERR_UNKNOWN_RESOURCE_TYPE;
+ }
+
+ /* append the resource */
+ for (next_resource = &state->resources; *next_resource; next_resource = &(*next_resource)->next)
+ ;
+ *next_resource = resource;
+
+ return MCERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ load_all_resources
+-------------------------------------------------*/
+
+static multicart_open_error load_all_resources(multicart_load_state *state)
+{
+ multicart_open_error err;
+ xml_data_node *resource_node;
+ multicart_resource_type resource_type;
+
+ for (resource_node = state->resources_node->child; resource_node != NULL; resource_node = resource_node->next)
+ {
+ resource_type = get_resource_type(resource_node->name);
+ if (resource_type != MULTICART_RESOURCE_TYPE_INVALID)
+ {
+ err = load_resource(state, resource_node, resource_type);
+ if (err != MCERR_NONE)
+ return err;
+ }
+ }
+
+ state->multicart->resources = state->resources;
+ return MCERR_NONE;
+}
+
+/*-------------------------------------------------
+ save_ram_resources. This is important for persistent RAM. All
+ resources were allocated within the memory pool of this device and will
+ be freed on multicart_close.
+-------------------------------------------------*/
+
+static multicart_open_error save_ram_resources(multicart_t *cart)
+{
+ const multicart_resource *resource;
+
+ for (resource = cart->resources; resource != NULL; resource = resource->next)
+ {
+ if ((resource->type == MULTICART_RESOURCE_TYPE_RAM) && (resource->filename != NULL))
+ {
+ image_battery_save_by_name(resource->filename, resource->ptr, resource->length);
+ }
+ }
+ return MCERR_NONE;
+}
+
+/*-------------------------------------------------
+ load_socket
+-------------------------------------------------*/
+
+static multicart_open_error load_socket(multicart_load_state *state, xml_data_node *socket_node)
+{
+ const char *id;
+ const char *uses;
+ const multicart_resource *resource;
+ multicart_socket *socket;
+ multicart_socket **next_socket;
+
+ /* get the 'id' and 'uses' attributes; error if not present */
+ id = xml_get_attribute_string(socket_node, "id", NULL);
+ uses = xml_get_attribute_string(socket_node, "uses", NULL);
+ if ((id == NULL) || (uses == NULL))
+ return MCERR_XML_ERROR;
+
+ /* find the resource */
+ for (resource = state->multicart->resources; resource != NULL; resource = resource->next)
+ {
+ if (!strcmp(uses, resource->id))
+ break;
+ }
+ if (resource == NULL)
+ return MCERR_INVALID_RESOURCE_REF;
+
+ /* create the socket */
+ socket = (multicart_socket *)pool_malloc_lib(state->multicart->data->pool, sizeof(*socket));
+ if (socket == NULL)
+ return MCERR_OUT_OF_MEMORY;
+ memset(socket, 0, sizeof(*socket));
+ socket->resource = resource;
+ socket->ptr = resource->ptr;
+
+ /* copy id */
+ socket->id = pool_strdup_lib(state->multicart->data->pool, id);
+ if (socket->id == NULL)
+ return MCERR_OUT_OF_MEMORY;
+
+ /* which pointer should I use? */
+ if (resource->ptr != NULL)
+ {
+ /* use the resource's ptr */
+ socket->ptr = resource->ptr;
+ }
+ else
+ {
+ /* allocate bytes for this socket */
+ socket->ptr = pool_malloc_lib(state->multicart->data->pool, resource->length);
+ if (socket->ptr == NULL)
+ return MCERR_OUT_OF_MEMORY;
+
+ /* ...and clear it */
+ memset(socket->ptr, 0xCD, resource->length);
+ }
+
+ /* append the resource */
+ for (next_socket = &state->sockets; *next_socket; next_socket = &(*next_socket)->next)
+ ;
+ *next_socket = socket;
+
+ return MCERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ load_all_sockets
+-------------------------------------------------*/
+
+static multicart_open_error load_all_sockets(multicart_load_state *state)
+{
+ multicart_open_error err;
+ xml_data_node *socket_node;
+
+ for (socket_node = xml_get_sibling(state->pcb_node->child, "socket"); socket_node != NULL;
+ socket_node = xml_get_sibling(socket_node->next, "socket"))
+ {
+ err = load_socket(state, socket_node);
+ if (err != MCERR_NONE)
+ return err;
+ }
+
+ state->multicart->sockets = state->sockets;
+ return MCERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ multicart_open - opens a multicart
+-------------------------------------------------*/
+
+multicart_open_error multicart_open(const char *filename, const char *gamedrv, multicart_load_flags load_flags, multicart_t **cart)
+{
+ multicart_open_error err;
+ zip_error ziperr;
+ object_pool *pool;
+ multicart_load_state state = {0, };
+ const zip_file_header *header;
+ const char *pcb_type;
+ char *layout_text = NULL;
+
+ /* allocate an object pool */
+ pool = pool_alloc_lib(NULL);
+ if (pool == NULL)
+ {
+ err = MCERR_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ /* allocate the multicart */
+ state.multicart = (multicart_t*)pool_malloc_lib(pool, sizeof(*state.multicart));
+ if (state.multicart == NULL)
+ {
+ err = MCERR_OUT_OF_MEMORY;
+ goto done;
+ }
+ memset(state.multicart, 0, sizeof(*state.multicart));
+
+ /* allocate the multicart's private data */
+ state.multicart->data = (multicart_private*)pool_malloc_lib(pool, sizeof(*state.multicart->data));
+ if (state.multicart->data == NULL)
+ {
+ err = MCERR_OUT_OF_MEMORY;
+ goto done;
+ }
+ memset(state.multicart->data, 0, sizeof(*state.multicart->data));
+ state.multicart->data->pool = pool;
+ pool = NULL;
+
+ /* open the ZIP file */
+ ziperr = zip_file_open(filename, &state.zip);
+ if (ziperr != ZIPERR_NONE)
+ {
+ err = MCERR_NOT_MULTICART;
+ goto done;
+ }
+
+ /* find the layout.xml file */
+ header = find_file(state.zip, "layout.xml");
+ if (header == NULL)
+ {
+ err = MCERR_MISSING_LAYOUT;
+ goto done;
+ }
+
+ /* reserve space for the layout text */
+ layout_text = (char*)malloc(header->uncompressed_length + 1);
+ if (layout_text == NULL)
+ {
+ err = MCERR_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ /* uncompress the layout text */
+ ziperr = zip_file_decompress(state.zip, layout_text, header->uncompressed_length);
+ if (ziperr != ZIPERR_NONE)
+ {
+ err = MCERR_ZIP_ERROR;
+ goto done;
+ }
+ layout_text[header->uncompressed_length] = '\0';
+
+ /* parse the layout text */
+ state.layout_xml = xml_string_read(layout_text, NULL);
+ if (state.layout_xml == NULL)
+ {
+ err = MCERR_XML_ERROR;
+ goto done;
+ }
+
+ /* locate the PCB node */
+ if (!find_pcb_and_resource_nodes(state.layout_xml, &state.pcb_node, &state.resources_node))
+ {
+ err = MCERR_NO_PCB_OR_RESOURCES;
+ goto done;
+ }
+
+ /* get the PCB resource_type */
+ pcb_type = xml_get_attribute_string(state.pcb_node, "type", "");
+ state.multicart->pcb_type = pool_strdup_lib(state.multicart->data->pool, pcb_type);
+ if (state.multicart->pcb_type == NULL)
+ {
+ err = MCERR_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ state.multicart->gamedrv_name = pool_strdup_lib(state.multicart->data->pool, gamedrv);
+ if (state.multicart->gamedrv_name == NULL)
+ {
+ err = MCERR_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ /* do we have to load resources? */
+ if (load_flags & MULTICART_FLAGS_LOAD_RESOURCES)
+ {
+ err = load_all_resources(&state);
+ if (err != MCERR_NONE)
+ goto done;
+
+ err = load_all_sockets(&state);
+ if (err != MCERR_NONE)
+ goto done;
+ }
+
+ err = MCERR_NONE;
+
+done:
+ if (pool != NULL)
+ pool_free_lib(pool);
+ if (state.zip != NULL)
+ zip_file_close(state.zip);
+ if (layout_text != NULL)
+ free(layout_text);
+ if (state.layout_xml != NULL)
+ xml_file_free(state.layout_xml);
+
+ if ((err != MCERR_NONE) && (state.multicart != NULL))
+ {
+ multicart_close(state.multicart);
+ state.multicart = NULL;
+ }
+ *cart = state.multicart;
+ return err;
+}
+
+
+/*-------------------------------------------------
+ multicart_close - closes a multicart
+-------------------------------------------------*/
+
+void multicart_close(multicart_t *cart)
+{
+ save_ram_resources(cart);
+ pool_free_lib(cart->data->pool);
+}
diff --git a/src/emu/imagedev/multcart.h b/src/emu/imagedev/multcart.h
new file mode 100644
index 00000000000..15697033e66
--- /dev/null
+++ b/src/emu/imagedev/multcart.h
@@ -0,0 +1,100 @@
+/*********************************************************************
+
+ multcart.h
+
+ Multi-cartridge handling code
+
+*********************************************************************/
+
+#ifndef __MULTCART_H__
+#define __MULTCART_H__
+
+#include "osdcore.h"
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+enum _multicart_load_flags
+{
+ MULTICART_FLAGS_DONT_LOAD_RESOURCES = 0x00,
+ MULTICART_FLAGS_LOAD_RESOURCES = 0x01
+};
+typedef enum _multicart_load_flags multicart_load_flags;
+
+enum _multicart_resource_type
+{
+ MULTICART_RESOURCE_TYPE_INVALID,
+ MULTICART_RESOURCE_TYPE_ROM,
+ MULTICART_RESOURCE_TYPE_RAM
+};
+typedef enum _multicart_resource_type multicart_resource_type;
+
+typedef struct _multicart_resource multicart_resource;
+struct _multicart_resource
+{
+ const char * id;
+ const char * filename;
+ multicart_resource * next;
+ multicart_resource_type type;
+ UINT32 length;
+ void * ptr;
+};
+
+
+typedef struct _multicart_socket multicart_socket;
+struct _multicart_socket
+{
+ const char * id;
+ multicart_socket * next;
+ const multicart_resource * resource;
+ void * ptr;
+};
+
+
+typedef struct _multicart_private multicart_private;
+
+typedef struct _multicart_t multicart_t;
+struct _multicart_t
+{
+ const multicart_resource * resources;
+ const multicart_socket * sockets;
+ const char * pcb_type;
+ const char * gamedrv_name; /* need this to find the path to the nvram files */
+ multicart_private * data;
+};
+
+
+enum _multicart_open_error
+{
+ MCERR_NONE,
+ MCERR_NOT_MULTICART,
+ MCERR_CORRUPT,
+ MCERR_OUT_OF_MEMORY,
+ MCERR_XML_ERROR,
+ MCERR_INVALID_FILE_REF,
+ MCERR_ZIP_ERROR,
+ MCERR_MISSING_RAM_LENGTH,
+ MCERR_INVALID_RAM_SPEC,
+ MCERR_UNKNOWN_RESOURCE_TYPE,
+ MCERR_INVALID_RESOURCE_REF,
+ MCERR_INVALID_FILE_FORMAT,
+ MCERR_MISSING_LAYOUT,
+ MCERR_NO_PCB_OR_RESOURCES
+};
+typedef enum _multicart_open_error multicart_open_error;
+
+const char *multicart_error_text(multicart_open_error error);
+
+/***************************************************************************
+ PROTOTYPES
+***************************************************************************/
+
+/* opens a multicart */
+multicart_open_error multicart_open(const char *filename, const char *drvname, multicart_load_flags load_flags, multicart_t **cart);
+
+/* closes a multicart */
+void multicart_close(multicart_t *cart);
+
+#endif /* __MULTCART_H__ */
diff --git a/src/emu/imagedev/printer.c b/src/emu/imagedev/printer.c
new file mode 100644
index 00000000000..e88a3f5a102
--- /dev/null
+++ b/src/emu/imagedev/printer.c
@@ -0,0 +1,116 @@
+/****************************************************************************
+
+ printer.c
+
+ Code for handling printer devices
+
+****************************************************************************/
+
+#include "emu.h"
+#include "printer.h"
+
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ printer_is_ready - checks to see if a printer
+ is ready
+-------------------------------------------------*/
+
+int printer_is_ready(device_t *printer)
+{
+ device_image_interface *image = dynamic_cast<device_image_interface *>(printer);
+ /* if there is a file attached to it, it's online */
+ return image->exists() != 0;
+}
+
+
+
+/*-------------------------------------------------
+ printer_output - outputs data to a printer
+-------------------------------------------------*/
+
+void printer_output(device_t *printer, UINT8 data)
+{
+ device_image_interface *image = dynamic_cast<device_image_interface *>(printer);
+ if (image->exists())
+ {
+ image->fwrite(&data, 1);
+ }
+}
+
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_LOAD( printer )
+-------------------------------------------------*/
+
+static DEVICE_IMAGE_LOAD( printer )
+{
+ const printer_config *conf = (const printer_config *)downcast<const legacy_image_device_config_base &>(image.device().baseconfig()).inline_config();
+
+ /* send notify that the printer is now online */
+ if (conf != NULL && conf->online != NULL)
+ conf->online(image, TRUE);
+
+ /* we don't need to do anything special */
+ return IMAGE_INIT_PASS;
+}
+
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_UNLOAD( printer )
+-------------------------------------------------*/
+
+static DEVICE_IMAGE_UNLOAD( printer )
+{
+ const printer_config *conf = (const printer_config *)downcast<const legacy_image_device_config_base &>(image.device().baseconfig()).inline_config();
+
+ /* send notify that the printer is now offline */
+ if (conf != NULL && conf->online != NULL)
+ conf->online(image, FALSE);
+}
+
+
+/*-------------------------------------------------
+ DEVICE_START(printer)
+-------------------------------------------------*/
+
+static DEVICE_START(printer)
+{
+}
+
+
+
+/*-------------------------------------------------
+ DEVICE_GET_INFO(printer)
+-------------------------------------------------*/
+
+DEVICE_GET_INFO(printer)
+{
+ switch(state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = 1; break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(printer_config); break;
+ case DEVINFO_INT_IMAGE_TYPE: info->i = IO_PRINTER; break;
+ case DEVINFO_INT_IMAGE_READABLE: info->i = 0; break;
+ case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_CREATABLE: info->i = 1; break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(printer); break;
+ case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(printer); break;
+ case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(printer); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Printer"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Printer"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, "prn"); break;
+ }
+}
+
+DEFINE_LEGACY_IMAGE_DEVICE(PRINTER, printer);
diff --git a/src/emu/imagedev/printer.h b/src/emu/imagedev/printer.h
new file mode 100644
index 00000000000..2c937d7aef5
--- /dev/null
+++ b/src/emu/imagedev/printer.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+
+ printer.h
+
+ Code for handling printer devices
+
+****************************************************************************/
+
+#ifndef __PRINTER_H__
+#define __PRINTER_H__
+
+#include "image.h"
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef void (*online_func)(device_t *device, int state);
+
+typedef struct _printer_config printer_config;
+struct _printer_config
+{
+ online_func online;
+};
+
+
+DECLARE_LEGACY_IMAGE_DEVICE(PRINTER, printer);
+
+
+#define MCFG_PRINTER_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, PRINTER, 0) \
+
+#define MCFG_PRINTER_ONLINE(_online) \
+ MCFG_DEVICE_CONFIG_DATAPTR(printer_config, online, _online)
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* checks to see if a printer is ready */
+int printer_is_ready(device_t *printer);
+
+/* outputs data to a printer */
+void printer_output(device_t *printer, UINT8 data);
+
+#endif /* __PRINTER_H__ */
diff --git a/src/emu/imagedev/snapquik.c b/src/emu/imagedev/snapquik.c
new file mode 100644
index 00000000000..460f2455cb9
--- /dev/null
+++ b/src/emu/imagedev/snapquik.c
@@ -0,0 +1,373 @@
+/*********************************************************************
+
+ snapquik.h
+
+ Snapshots and quickloads
+
+*********************************************************************/
+
+#include "emu.h"
+#include "snapquik.h"
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _snapquick_token snapquick_token;
+struct _snapquick_token
+{
+ emu_timer *timer;
+ snapquick_load_func load;
+};
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ assert_is_snapshot_or_quickload - asserts/confirms
+ that a given device is a snapshot or quickload
+-------------------------------------------------*/
+
+INLINE void assert_is_snapshot_or_quickload(device_t *device)
+{
+ assert(device != NULL);
+ assert(downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config() != NULL);
+ assert((device->type() == SNAPSHOT) || (device->type() == QUICKLOAD)
+ || (device->type() == Z80BIN));
+}
+
+
+
+/*-------------------------------------------------
+ get_token - safely gets the snapshot/quickload data
+-------------------------------------------------*/
+
+INLINE snapquick_token *get_token(device_t *device)
+{
+ assert_is_snapshot_or_quickload(device);
+ return (snapquick_token *) downcast<legacy_device_base *>(device)->token();
+}
+
+
+
+/*-------------------------------------------------
+ get_config - safely gets the quickload config
+-------------------------------------------------*/
+
+INLINE const snapquick_config *get_config(device_t *device)
+{
+ assert_is_snapshot_or_quickload(device);
+ return (const snapquick_config *) downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config();
+}
+
+INLINE const snapquick_config *get_config_dev(const device_config *device)
+{
+ assert(device != NULL);
+ assert((device->type() == SNAPSHOT) || (device->type() == QUICKLOAD)
+ || (device->type() == Z80BIN));
+ return (const snapquick_config *) downcast<const legacy_device_config_base *>(device)->inline_config();
+}
+
+/*-------------------------------------------------
+ log_quickload - logs and displays useful
+ data for the end user
+-------------------------------------------------*/
+
+void log_quickload(const char *type, UINT32 start, UINT32 length, UINT32 exec, const char *exec_format)
+{
+ astring tempstring;
+
+ logerror("Loading %04X bytes of RAM at %04X\n", length, start);
+
+ tempstring.catprintf("Quickload type: %s Length: %d bytes\n", type, length);
+ tempstring.catprintf("Start: 0x%04X End: 0x%04X Exec: ", start, start + length - 1);
+
+ logerror("Quickload loaded.\n");
+ if (!mame_stricmp(exec_format, EXEC_NA))
+ tempstring.cat("N/A");
+ else
+ {
+ logerror("Execution can resume with ");
+ logerror(exec_format, exec);
+ logerror("\n");
+ tempstring.catprintf(exec_format, exec);
+ }
+
+ ui_popup_time(10, "%s", tempstring.cstr());
+}
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ TIMER_CALLBACK(process_snapshot_or_quickload)
+-------------------------------------------------*/
+
+static TIMER_CALLBACK(process_snapshot_or_quickload)
+{
+ device_image_interface *image = (device_image_interface *) ptr;
+ snapquick_token *token = get_token(&image->device());
+
+ /* invoke the load */
+ (*token->load)(*image,
+ image->filetype(),
+ image->length());
+
+ /* unload the device */
+ image->unload();
+}
+
+
+
+/*-------------------------------------------------
+ DEVICE_START( snapquick )
+-------------------------------------------------*/
+
+static DEVICE_START( snapquick )
+{
+ snapquick_token *token = get_token(device);
+
+ /* allocate a timer */
+ token->timer = timer_alloc(device->machine, process_snapshot_or_quickload, (void *) dynamic_cast<device_image_interface *>(device));
+
+}
+
+
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_LOAD( snapquick )
+-------------------------------------------------*/
+
+static DEVICE_IMAGE_LOAD( snapquick )
+{
+ const snapquick_config *config = get_config(image);
+ snapquick_token *token = get_token(image);
+
+ /* locate the load function */
+ token->load = (snapquick_load_func) reinterpret_cast<snapquick_load_func>(image.get_device_specific_call());
+
+ /* adjust the timer */
+ timer_adjust_oneshot(
+ token->timer,
+ attotime_make(config->delay_seconds, config->delay_attoseconds),
+ 0);
+
+ return IMAGE_INIT_PASS;
+}
+
+
+
+/*-------------------------------------------------
+ DEVICE_GET_INFO(snapquick) - device getinfo
+ function
+-------------------------------------------------*/
+
+static DEVICE_GET_INFO(snapquick)
+{
+ switch(state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(snapquick_token); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(snapquick_config); break;
+ case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 0; break;
+ case DEVINFO_INT_IMAGE_CREATABLE: info->i = 0; break;
+
+ /* --- the following bits of info are returned as pointers to functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(snapquick); break;
+ case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(snapquick); break;
+ case DEVINFO_FCT_SNAPSHOT_QUICKLOAD_LOAD: info->f = (genf *) get_config_dev(device)->load; break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, get_config_dev(device)->file_extensions); break;
+ }
+}
+
+
+
+/*-------------------------------------------------
+ DEVICE_GET_INFO(snapshot) - device getinfo
+ function
+-------------------------------------------------*/
+
+DEVICE_GET_INFO(snapshot)
+{
+ switch(state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_IMAGE_TYPE: info->i = IO_SNAPSHOT; break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Snapshot"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Snapshot"); break;
+
+ default: DEVICE_GET_INFO_CALL(snapquick); break;
+ }
+}
+
+
+
+/*-------------------------------------------------
+ DEVICE_GET_INFO(quickload) - device getinfo
+ function
+-------------------------------------------------*/
+
+DEVICE_GET_INFO(quickload)
+{
+ switch(state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_IMAGE_TYPE: info->i = IO_QUICKLOAD; break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Quickload"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Quickload"); break;
+
+ default: DEVICE_GET_INFO_CALL(snapquick); break;
+ }
+}
+/*-------------------------------------------------
+ z80bin_load_file - load a z80bin file into
+ memory
+-------------------------------------------------*/
+
+static int z80bin_load_file(device_image_interface *image, const char *file_type, UINT16 *exec_addr, UINT16 *start_addr, UINT16 *end_addr )
+{
+ int ch;
+ UINT16 args[3];
+ UINT16 i=0, j, size;
+ UINT8 data;
+ char pgmname[256];
+ char message[256];
+
+ image->fseek(7, SEEK_SET);
+
+ while((ch = image->fgetc()) != 0x1A)
+ {
+ if (ch == EOF)
+ {
+ image->seterror(IMAGE_ERROR_INVALIDIMAGE, "Unexpected EOF while getting file name");
+ image->message(" Unexpected EOF while getting file name");
+ return IMAGE_INIT_FAIL;
+ }
+
+ if (ch != '\0')
+ {
+ if (i >= (ARRAY_LENGTH(pgmname) - 1))
+ {
+ image->seterror(IMAGE_ERROR_INVALIDIMAGE, "File name too long");
+ image->message(" File name too long");
+ return IMAGE_INIT_FAIL;
+ }
+
+ pgmname[i] = ch; /* build program name */
+ i++;
+ }
+ }
+
+ pgmname[i] = '\0'; /* terminate string with a null */
+
+ if (image->fread(args, sizeof(args)) != sizeof(args))
+ {
+ image->seterror(IMAGE_ERROR_INVALIDIMAGE, "Unexpected EOF while getting file size");
+ image->message(" Unexpected EOF while getting file size");
+ return IMAGE_INIT_FAIL;
+ }
+
+ exec_addr[0] = LITTLE_ENDIANIZE_INT16(args[0]);
+ start_addr[0] = LITTLE_ENDIANIZE_INT16(args[1]);
+ end_addr[0] = LITTLE_ENDIANIZE_INT16(args[2]);
+
+ size = (end_addr[0] - start_addr[0] + 1) & 0xffff;
+
+ /* display a message about the loaded quickload */
+ image->message(" %s\nsize=%04X : start=%04X : end=%04X : exec=%04X",pgmname,size,start_addr[0],end_addr[0],exec_addr[0]);
+
+ for (i = 0; i < size; i++)
+ {
+ j = (start_addr[0] + i) & 0xffff;
+ if (image->fread(&data, 1) != 1)
+ {
+ snprintf(message, ARRAY_LENGTH(message), "%s: Unexpected EOF while writing byte to %04X", pgmname, (unsigned) j);
+ image->seterror(IMAGE_ERROR_INVALIDIMAGE, message);
+ image->message("%s: Unexpected EOF while writing byte to %04X", pgmname, (unsigned) j);
+ return IMAGE_INIT_FAIL;
+ }
+ cputag_get_address_space(image->device().machine,"maincpu",ADDRESS_SPACE_PROGRAM)->write_byte(j, data);
+ }
+
+ return IMAGE_INIT_PASS;
+}
+
+
+
+/*-------------------------------------------------
+ QUICKLOAD_LOAD( z80bin )
+-------------------------------------------------*/
+
+static QUICKLOAD_LOAD( z80bin )
+{
+ const z80bin_config *config;
+ UINT16 exec_addr, start_addr, end_addr;
+ int autorun;
+
+ /* load the binary into memory */
+ if (z80bin_load_file(&image, file_type, &exec_addr, &start_addr, &end_addr) == IMAGE_INIT_FAIL)
+ return IMAGE_INIT_FAIL;
+
+ /* is this file executable? */
+ if (exec_addr != 0xffff)
+ {
+ config = (const z80bin_config *)downcast<const legacy_device_config_base &>(image.device().baseconfig()).inline_config();
+
+ /* check to see if autorun is on (I hate how this works) */
+ autorun = input_port_read_safe(image.device().machine, "CONFIG", 0xFF) & 1;
+
+ /* start program */
+ if (config->execute != NULL)
+ {
+ (*config->execute)(image.device().machine, start_addr, end_addr, exec_addr, autorun);
+ }
+ else
+ {
+ if (autorun)
+ cpu_set_reg(image.device().machine->device("maincpu"), STATE_GENPC, exec_addr);
+ }
+ }
+
+ return IMAGE_INIT_PASS;
+}
+
+
+
+/*-------------------------------------------------
+ DEVICE_GET_INFO(z80bin)
+-------------------------------------------------*/
+
+DEVICE_GET_INFO(z80bin)
+{
+ /* quickload */
+ switch(state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(z80bin_config); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, "bin"); break;
+
+ /* --- the following bits of info are returned as pointers to functions --- */
+ case DEVINFO_FCT_SNAPSHOT_QUICKLOAD_LOAD: info->f = (genf *) quickload_load_z80bin; break;
+
+ default: DEVICE_GET_INFO_CALL(quickload); break;
+ }
+}
+
+DEFINE_LEGACY_IMAGE_DEVICE(Z80BIN, z80bin);
+DEFINE_LEGACY_IMAGE_DEVICE(SNAPSHOT, snapshot);
+DEFINE_LEGACY_IMAGE_DEVICE(QUICKLOAD, quickload);
diff --git a/src/emu/imagedev/snapquik.h b/src/emu/imagedev/snapquik.h
new file mode 100644
index 00000000000..db9bab25259
--- /dev/null
+++ b/src/emu/imagedev/snapquik.h
@@ -0,0 +1,115 @@
+/*********************************************************************
+
+ snapquik.h
+
+ Snapshots and quickloads
+
+*********************************************************************/
+
+#ifndef __SNAPQUIK_H__
+#define __SNAPQUIK_H__
+
+#include "image.h"
+#include "ui.h"
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+enum
+{
+ DEVINFO_FCT_SNAPSHOT_QUICKLOAD_LOAD = DEVINFO_FCT_DEVICE_SPECIFIC
+};
+
+
+
+/***************************************************************************
+ MACROS
+***************************************************************************/
+
+DECLARE_LEGACY_IMAGE_DEVICE(SNAPSHOT, snapshot);
+DECLARE_LEGACY_IMAGE_DEVICE(QUICKLOAD, quickload);
+DECLARE_LEGACY_IMAGE_DEVICE(Z80BIN, z80bin);
+
+#define SNAPSHOT_LOAD_NAME(name) snapshot_load_##name
+#define SNAPSHOT_LOAD(name) int SNAPSHOT_LOAD_NAME(name)(device_image_interface &image, const char *file_type, int snapshot_size)
+
+#define QUICKLOAD_LOAD_NAME(name) quickload_load_##name
+#define QUICKLOAD_LOAD(name) int QUICKLOAD_LOAD_NAME(name)(device_image_interface &image, const char *file_type, int quickload_size)
+
+#define Z80BIN_EXECUTE_NAME(name) z80bin_execute_##name
+#define Z80BIN_EXECUTE(name) void Z80BIN_EXECUTE_NAME(name)(running_machine *machine, UINT16 start_address, UINT16 end_address, UINT16 execute_address, int autorun)
+
+#define LOAD_REG(_cpu, _reg, _data) \
+ do { \
+ cpu_set_reg(_cpu, _reg, (_data)); \
+ } while (0)
+
+#define EXEC_NA "N/A"
+#define z80bin_execute_default NULL
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef int (*snapquick_load_func)(device_image_interface &image, const char *file_type, int file_size);
+
+typedef struct _snapquick_config snapquick_config;
+struct _snapquick_config
+{
+ snapquick_load_func load; /* loading function */
+ const char * file_extensions; /* file extensions */
+ seconds_t delay_seconds; /* loading delay (seconds) */
+ attoseconds_t delay_attoseconds; /* loading delay (attoseconds) */
+};
+
+typedef void (*z80bin_execute_func)(running_machine *machine, UINT16 start_address, UINT16 end_address, UINT16 execute_address, int autorun);
+
+typedef struct _z80bin_config z80bin_config;
+struct _z80bin_config
+{
+ snapquick_config base;
+ z80bin_execute_func execute;
+};
+
+/***************************************************************************
+ SNAPSHOT DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_SNAPSHOT_ADD(_tag, _load, _file_extensions, _delay) \
+ MCFG_DEVICE_ADD(_tag, SNAPSHOT, 0) \
+ MCFG_DEVICE_CONFIG_DATAPTR(snapquick_config, load, SNAPSHOT_LOAD_NAME(_load)) \
+ MCFG_DEVICE_CONFIG_DATAPTR(snapquick_config, file_extensions, _file_extensions) \
+ MCFG_DEVICE_CONFIG_DATA64(snapquick_config, delay_seconds, (seconds_t) (_delay)) \
+ MCFG_DEVICE_CONFIG_DATA64(snapquick_config, delay_attoseconds, (attoseconds_t) (((_delay) - (int)(_delay)) * ATTOSECONDS_PER_SECOND)) \
+
+/***************************************************************************
+ QUICKLOAD DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_QUICKLOAD_ADD(_tag, _load, _file_extensions, _delay) \
+ MCFG_DEVICE_ADD(_tag, QUICKLOAD, 0) \
+ MCFG_DEVICE_CONFIG_DATAPTR(snapquick_config, load, QUICKLOAD_LOAD_NAME(_load)) \
+ MCFG_DEVICE_CONFIG_DATAPTR(snapquick_config, file_extensions, _file_extensions) \
+ MCFG_DEVICE_CONFIG_DATA64(snapquick_config, delay_seconds, (seconds_t) (_delay)) \
+ MCFG_DEVICE_CONFIG_DATA64(snapquick_config, delay_attoseconds, (attoseconds_t) (((_delay) - (int)(_delay)) * ATTOSECONDS_PER_SECOND)) \
+
+/***************************************************************************
+ Z80BIN QUICKLOAD DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_Z80BIN_QUICKLOAD_ADD(_tag, _execute, _delay) \
+ MCFG_DEVICE_ADD(_tag, Z80BIN, 0) \
+ MCFG_DEVICE_CONFIG_DATA64(snapquick_config, delay_seconds, (seconds_t) (_delay)) \
+ MCFG_DEVICE_CONFIG_DATA64(snapquick_config, delay_attoseconds, (attoseconds_t) (((_delay) - (int)(_delay)) * ATTOSECONDS_PER_SECOND)) \
+ MCFG_DEVICE_CONFIG_DATAPTR(z80bin_config, execute, Z80BIN_EXECUTE_NAME(_execute))
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+void log_quickload(const char *type, UINT32 start, UINT32 length, UINT32 exec, const char *exec_format);
+
+
+#endif /* __SNAPQUIK_H__ */
diff --git a/src/emu/imagedev/wavfile.c b/src/emu/imagedev/wavfile.c
new file mode 100644
index 00000000000..aa8d17e8a74
--- /dev/null
+++ b/src/emu/imagedev/wavfile.c
@@ -0,0 +1,291 @@
+/*********************************************************************
+
+ wavfile.c
+
+ Format code for wave (*.wav) files
+
+*********************************************************************/
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "wavfile.h"
+#include "cassimg.h"
+
+static const char magic1[4] = { 'R', 'I', 'F', 'F' };
+static const char magic2[4] = { 'W', 'A', 'V', 'E' };
+static const char format_tag_id[4] = { 'f', 'm', 't', ' ' };
+static const char data_tag_id[4] = { 'd', 'a', 't', 'a' };
+
+#define WAV_FORMAT_PCM 1
+
+
+
+static UINT32 get_leuint32(const void *ptr)
+{
+ UINT32 value;
+ memcpy(&value, ptr, sizeof(value));
+ return LITTLE_ENDIANIZE_INT32(value);
+}
+
+
+
+static UINT16 get_leuint16(const void *ptr)
+{
+ UINT16 value;
+ memcpy(&value, ptr, sizeof(value));
+ return LITTLE_ENDIANIZE_INT16(value);
+}
+
+
+
+static void put_leuint32(void *ptr, UINT32 value)
+{
+ value = LITTLE_ENDIANIZE_INT32(value);
+ memcpy(ptr, &value, sizeof(value));
+}
+
+
+
+static void put_leuint16(void *ptr, UINT16 value)
+{
+ value = LITTLE_ENDIANIZE_INT16(value);
+ memcpy(ptr, &value, sizeof(value));
+}
+
+
+
+static casserr_t wavfile_process(cassette_image *cassette, struct CassetteOptions *opts,
+ int read_waveform)
+{
+ UINT8 file_header[12];
+ UINT8 tag_header[8];
+ UINT8 format_tag[16];
+ UINT32 stated_size;
+ UINT64 file_size;
+ UINT32 tag_size;
+ UINT32 tag_samples;
+ UINT64 offset;
+ int format_specified = FALSE;
+
+ UINT16 format_type = 0;
+ UINT32 bytes_per_second = 0;
+ UINT16 block_align = 0;
+ int waveform_flags = 0;
+
+ /* read header */
+ cassette_image_read(cassette, file_header, 0, sizeof(file_header));
+ offset = sizeof(file_header);
+
+ /* check magic numbers */
+ if (memcmp(&file_header[0], magic1, 4))
+ return CASSETTE_ERROR_INVALIDIMAGE;
+ if (memcmp(&file_header[8], magic2, 4))
+ return CASSETTE_ERROR_INVALIDIMAGE;
+
+ /* read and sanity check size */
+ stated_size = get_leuint32(&file_header[4]) + 8;
+ file_size = cassette_image_size(cassette);
+ if (stated_size > file_size)
+ stated_size = (UINT32) file_size;
+
+ while(offset < stated_size)
+ {
+ cassette_image_read(cassette, tag_header, offset, sizeof(tag_header));
+ tag_size = get_leuint32(&tag_header[4]);
+ offset += sizeof(tag_header);
+
+ if (!memcmp(tag_header, format_tag_id, 4))
+ {
+ /* format tag */
+ if (format_specified || (tag_size < sizeof(format_tag)))
+ return CASSETTE_ERROR_INVALIDIMAGE;
+ format_specified = TRUE;
+
+ cassette_image_read(cassette, format_tag, offset, sizeof(format_tag));
+
+ format_type = get_leuint16(&format_tag[0]);
+ opts->channels = get_leuint16(&format_tag[2]);
+ opts->sample_frequency = get_leuint32(&format_tag[4]);
+ bytes_per_second = get_leuint32(&format_tag[8]);
+ block_align = get_leuint16(&format_tag[12]);
+ opts->bits_per_sample = get_leuint16(&format_tag[14]);
+
+ if (format_type != WAV_FORMAT_PCM)
+ return CASSETTE_ERROR_INVALIDIMAGE;
+ if (opts->sample_frequency * opts->bits_per_sample * opts->channels / 8 != bytes_per_second)
+ return CASSETTE_ERROR_INVALIDIMAGE;
+
+ switch(opts->bits_per_sample)
+ {
+ case 8:
+ waveform_flags = CASSETTE_WAVEFORM_8BIT;
+ break;
+ case 16:
+ waveform_flags = CASSETTE_WAVEFORM_16BITLE;
+ break;
+ case 32:
+ waveform_flags = CASSETTE_WAVEFORM_32BITLE;
+ break;
+ default:
+ return CASSETTE_ERROR_INVALIDIMAGE;
+ }
+ }
+ else if (!memcmp(tag_header, data_tag_id, 4))
+ {
+ /* data tag */
+ if (!format_specified)
+ return CASSETTE_ERROR_INVALIDIMAGE;
+
+ if (read_waveform)
+ {
+ tag_samples = tag_size / (opts->bits_per_sample / 8) / opts->channels;
+ cassette_read_samples(cassette, opts->channels, 0.0, tag_samples / ((double) opts->sample_frequency),
+ tag_samples, offset, waveform_flags);
+ }
+ }
+ else
+ {
+ /* ignore other tags */
+ }
+ offset += tag_size;
+ }
+
+ return CASSETTE_ERROR_SUCCESS;
+}
+
+
+
+static casserr_t wavfile_identify(cassette_image *cassette, struct CassetteOptions *opts)
+{
+ return wavfile_process(cassette, opts, FALSE);
+}
+
+
+
+static casserr_t wavfile_load(cassette_image *cassette)
+{
+ struct CassetteOptions opts;
+ memset(&opts, 0, sizeof(opts));
+ return wavfile_process(cassette, &opts, TRUE);
+}
+
+
+
+static casserr_t wavfile_save(cassette_image *cassette, const struct CassetteInfo *info)
+{
+ casserr_t err;
+ UINT8 consolidated_header[12 + 8 + 16 + 8];
+ UINT8 *header = &consolidated_header[0];
+ UINT8 *format_tag_header = &consolidated_header[12];
+ UINT8 *format_tag_data = &consolidated_header[12 + 8];
+ UINT8 *data_tag_header = &consolidated_header[12 + 8 + 16];
+ UINT32 file_size;
+ UINT32 bytes_per_second;
+ UINT16 bits_per_sample;
+ UINT32 data_size;
+ size_t bytes_per_sample = 2;
+ int waveform_flags = CASSETTE_WAVEFORM_16BITLE;
+ UINT16 block_align;
+
+ bits_per_sample = (UINT16) (bytes_per_sample * 8);
+ bytes_per_second = info->sample_frequency * bytes_per_sample * info->channels;
+ data_size = (UINT32) (info->sample_count * bytes_per_sample * info->channels);
+ file_size = data_size + sizeof(consolidated_header) - 8;
+ block_align = (UINT16) (bytes_per_sample * info->channels);
+
+ /* set up header */
+ memcpy(&header[0], magic1, 4);
+ memcpy(&header[8], magic2, 4);
+ put_leuint32(&header[4], file_size);
+
+ /* set up format tag */
+ memcpy(&format_tag_header[0], format_tag_id, 4);
+ put_leuint32(&format_tag_header[4], 16);
+ put_leuint16(&format_tag_data[0], WAV_FORMAT_PCM);
+ put_leuint16(&format_tag_data[2], info->channels);
+ put_leuint32(&format_tag_data[4], info->sample_frequency);
+ put_leuint32(&format_tag_data[8], bytes_per_second);
+ put_leuint16(&format_tag_data[12], block_align);
+ put_leuint16(&format_tag_data[14], bits_per_sample);
+
+ /* set up data tag */
+ memcpy(&data_tag_header[0], data_tag_id, 4);
+ put_leuint32(&data_tag_header[4], data_size);
+
+ /* write consolidated header */
+ cassette_image_write(cassette, consolidated_header, 0, sizeof(consolidated_header));
+
+ /* write out the actual data */
+ err = cassette_write_samples(cassette, info->channels, 0.0, info->sample_count
+ / (double) info->sample_frequency, info->sample_count, sizeof(consolidated_header),
+ waveform_flags);
+ if (err)
+ return err;
+
+ return CASSETTE_ERROR_SUCCESS;
+}
+
+
+
+const struct CassetteFormat wavfile_format =
+{
+ "wav",
+ wavfile_identify,
+ wavfile_load,
+ wavfile_save
+};
+
+
+
+/*********************************************************************
+ wavfile_testload()
+
+ This is a hokey function used to test the cassette wave loading
+ system, specifically to test that when one loads a WAV file image
+ that the resulting info queried will be the same data in the WAV.
+
+ This code has already identified some rounding errors
+*********************************************************************/
+
+#ifdef UNUSED_FUNCTION
+void wavfile_testload(const char *fname)
+{
+ cassette_image *cassette;
+ FILE *f;
+ long offset;
+ int freq, samples, i;
+ INT32 cassamp;
+ INT16 wavsamp;
+
+ f = fopen(fname, "rb");
+ if (!f)
+ return;
+
+ if (cassette_open(f, &stdio_ioprocs, &wavfile_format, CASSETTE_FLAG_READONLY, &cassette))
+ {
+ fclose(f);
+ return;
+ }
+
+ offset = 44;
+ freq = 44100;
+ samples = 5667062;
+
+ for (i = 0; i < samples; i++)
+ {
+ cassette_get_sample(cassette, 0, i / (double) freq, 0.0, &cassamp);
+
+ fseek(f, offset + i * 2, SEEK_SET);
+ fread(&wavsamp, 1, 2, f);
+ assert(cassamp == (((UINT32) wavsamp) << 16));
+ }
+
+ cassette_close(cassette);
+
+ fclose(f);
+}
+#endif
+
+
+
diff --git a/src/emu/imagedev/wavfile.h b/src/emu/imagedev/wavfile.h
new file mode 100644
index 00000000000..ab769de5537
--- /dev/null
+++ b/src/emu/imagedev/wavfile.h
@@ -0,0 +1,16 @@
+/*********************************************************************
+
+ wavfile.h
+
+ Format code for wave (*.wav) files
+
+*********************************************************************/
+
+#ifndef WAVFILE_H
+#define WAVFILE_H
+
+#include "cassimg.h"
+
+extern const struct CassetteFormat wavfile_format;
+
+#endif /* WAVFILE_H */
diff --git a/src/emu/machine/idectrl.c b/src/emu/machine/idectrl.c
index 1cfdd847a70..248281c61ee 100644
--- a/src/emu/machine/idectrl.c
+++ b/src/emu/machine/idectrl.c
@@ -7,10 +7,7 @@
#include "emu.h"
#include "idectrl.h"
#include "debugger.h"
-
-#ifdef MESS
-#include "devices/harddriv.h"
-#endif
+#include "imagedev/harddriv.h"
/***************************************************************************
DEBUGGING
diff --git a/src/emu/machine/scsicd.c b/src/emu/machine/scsicd.c
index 3a67cb33d49..3a6113ccf94 100644
--- a/src/emu/machine/scsicd.c
+++ b/src/emu/machine/scsicd.c
@@ -8,9 +8,7 @@
#include "scsidev.h"
#include "cdrom.h"
#include "sound/cdda.h"
-#ifdef MESS
-#include "devices/chd_cd.h"
-#endif
+#include "imagedev/chd_cd.h"
#include "scsicd.h"
typedef struct
diff --git a/src/emu/machine/scsihd.c b/src/emu/machine/scsihd.c
index 039243077c0..dbdd474b2fa 100644
--- a/src/emu/machine/scsihd.c
+++ b/src/emu/machine/scsihd.c
@@ -7,10 +7,7 @@
#include "emu.h"
#include "scsidev.h"
#include "harddisk.h"
-
-#ifdef MESS
-#include "devices/harddriv.h"
-#endif
+#include "imagedev/harddriv.h"
#include "scsihd.h"
typedef struct
diff --git a/src/emu/sound/wave.c b/src/emu/sound/wave.c
index 2bf3cf00d5e..8b854b7a30c 100644
--- a/src/emu/sound/wave.c
+++ b/src/emu/sound/wave.c
@@ -15,16 +15,13 @@
#include "emu.h"
#include "streams.h"
-#ifdef MESS
-#include "devices/cassette.h"
-#endif
+#include "imagedev/cassette.h"
#include "wave.h"
#define ALWAYS_PLAY_SOUND 0
static STREAM_UPDATE( wave_sound_update )
{
-#ifdef MESS
device_image_interface *image = (device_image_interface *)param;
int speakers = speaker_output_count(image->device().machine->config);
cassette_image *cassette;
@@ -62,7 +59,6 @@ static STREAM_UPDATE( wave_sound_update )
if (speakers > 1)
memset(right_buffer, 0, sizeof(*right_buffer) * samples);
}
-#endif
}
@@ -74,9 +70,7 @@ static DEVICE_START( wave )
assert( device != NULL );
assert( device->baseconfig().static_config() != NULL );
int speakers = speaker_output_count(device->machine->config);
-#ifdef MESS
image = dynamic_cast<device_image_interface *>(device->machine->device( (const char *)device->baseconfig().static_config()));
-#endif
if (speakers > 1)
stream_create(device, 0, 2, device->machine->sample_rate, (void *)image, wave_sound_update);
else
diff --git a/src/emu/uiimage.c b/src/emu/uiimage.c
index 2c2205d5bca..497f25fa6f4 100644
--- a/src/emu/uiimage.c
+++ b/src/emu/uiimage.c
@@ -23,6 +23,8 @@
#include "uimenu.h"
#include "zippath.h"
#include "unicode.h"
+#include "imagedev/cassette.h"
+#include "imagedev/bitbngr.h"
@@ -1014,3 +1016,426 @@ void ui_image_menu_image_info(running_machine *machine, ui_menu *menu, void *par
/* process the menu */
ui_menu_process(machine, menu, 0);
}
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+#define TAPECMD_NULL ((void *) 0x0000)
+#define TAPECMD_STOP ((void *) 0x0001)
+#define TAPECMD_PLAY ((void *) 0x0002)
+#define TAPECMD_RECORD ((void *) 0x0003)
+#define TAPECMD_REWIND ((void *) 0x0004)
+#define TAPECMD_FAST_FORWARD ((void *) 0x0005)
+#define TAPECMD_SLIDER ((void *) 0x0006)
+#define TAPECMD_SELECT ((void *) 0x0007)
+
+#define BITBANGERCMD_SELECT ((void *) 0x0000)
+#define BITBANGERCMD_MODE ((void *) 0x0001)
+#define BITBANGERCMD_BAUD ((void *) 0x0002)
+#define BITBANGERCMD_TUNE ((void *) 0x0003)
+
+
+typedef struct _tape_control_menu_state tape_control_menu_state;
+struct _tape_control_menu_state
+{
+ int index;
+ device_image_interface *device;
+};
+
+
+
+typedef struct _bitbanger_control_menu_state bitbanger_control_menu_state;
+struct _bitbanger_control_menu_state
+{
+ int index;
+ device_image_interface *device;
+};
+
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ cassette_count - returns the number of cassette
+ devices in the machine
+-------------------------------------------------*/
+
+INLINE int cassette_count( running_machine *machine )
+{
+ int count = 0;
+ device_t *device = machine->m_devicelist.first(CASSETTE);
+
+ while ( device )
+ {
+ count++;
+ device = device->typenext();
+ }
+ return count;
+}
+
+/*-------------------------------------------------
+ bitbanger_count - returns the number of bitbanger
+ devices in the machine
+-------------------------------------------------*/
+
+INLINE int bitbanger_count( running_machine *machine )
+{
+ int count = 0;
+ device_t *device = machine->m_devicelist.first(BITBANGER);
+
+ while ( device )
+ {
+ count++;
+ device = device->typenext();
+ }
+ return count;
+}
+
+/*-------------------------------------------------
+ tapecontrol_gettime - returns a textual
+ representation of the time
+-------------------------------------------------*/
+
+astring *tapecontrol_gettime(astring *dest, device_t *device, int *curpos, int *endpos)
+{
+ double t0, t1;
+
+ t0 = cassette_get_position(device);
+ t1 = cassette_get_length(device);
+
+ if (t1)
+ astring_printf(dest, "%04d/%04d", (int) t0, (int) t1);
+ else
+ astring_printf(dest, "%04d/%04d", 0, (int) t1);
+
+ if (curpos != NULL)
+ *curpos = t0;
+ if (endpos != NULL)
+ *endpos = t1;
+
+ return dest;
+}
+
+
+
+/*-------------------------------------------------
+ menu_tape_control_populate - populates the
+ main tape control menu
+-------------------------------------------------*/
+
+static void menu_tape_control_populate(running_machine *machine, ui_menu *menu, tape_control_menu_state *menustate)
+{
+ astring timepos;
+ cassette_state state;
+ int count = cassette_count(machine);
+ UINT32 flags = 0;
+
+ if( count > 0 )
+ {
+ if( menustate->index == (count-1) )
+ flags |= MENU_FLAG_LEFT_ARROW;
+ else
+ flags |= MENU_FLAG_RIGHT_ARROW;
+ }
+
+ if (menustate->device->exists())
+ {
+ double t0, t1;
+ UINT32 tapeflags = 0;
+
+ t0 = cassette_get_position(&menustate->device->device());
+ t1 = cassette_get_length(&menustate->device->device());
+
+ if (t1 > 0)
+ {
+ if (t0 > 0)
+ tapeflags |= MENU_FLAG_LEFT_ARROW;
+ if (t0 < t1)
+ tapeflags |= MENU_FLAG_RIGHT_ARROW;
+ }
+
+ /* name of tape */
+ ui_menu_item_append(menu, menustate->device->image_config().devconfig().name(), menustate->device->filename(), flags, TAPECMD_SELECT);
+
+ /* state */
+ tapecontrol_gettime(&timepos, &menustate->device->device(), NULL, NULL);
+ state = cassette_get_state(&menustate->device->device());
+ ui_menu_item_append(
+ menu,
+ (state & CASSETTE_MASK_UISTATE) == CASSETTE_STOPPED
+ ? "stopped"
+ : ((state & CASSETTE_MASK_UISTATE) == CASSETTE_PLAY
+ ? ((state & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_ENABLED ? "playing" : "(playing)")
+ : ((state & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_ENABLED ? "recording" : "(recording)")
+ ),
+ astring_c(&timepos),
+ tapeflags,
+ TAPECMD_SLIDER);
+
+ /* pause or stop */
+ ui_menu_item_append(menu, "Pause/Stop", NULL, 0, TAPECMD_STOP);
+
+ /* play */
+ ui_menu_item_append(menu, "Play", NULL, 0, TAPECMD_PLAY);
+
+ /* record */
+ ui_menu_item_append(menu, "Record", NULL, 0, TAPECMD_RECORD);
+
+ /* rewind */
+ ui_menu_item_append(menu, "Rewind", NULL, 0, TAPECMD_REWIND);
+
+ /* fast forward */
+ ui_menu_item_append(menu, "Fast Forward", NULL, 0, TAPECMD_FAST_FORWARD);
+ }
+ else
+ {
+ /* no tape loaded */
+ ui_menu_item_append(menu, "No Tape Image loaded", NULL, flags, NULL);
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_bitbanger_control_populate - populates the
+ main bitbanger control menu
+-------------------------------------------------*/
+
+static void menu_bitbanger_control_populate(running_machine *machine, ui_menu *menu, bitbanger_control_menu_state *menustate)
+{
+ int count = bitbanger_count(machine);
+ UINT32 flags = 0, mode_flags = 0, baud_flags = 0, tune_flags = 0;
+
+ if( count > 0 )
+ {
+ if( menustate->index == (count-1) )
+ flags |= MENU_FLAG_LEFT_ARROW;
+ else
+ flags |= MENU_FLAG_RIGHT_ARROW;
+ }
+
+ if (bitbanger_inc_mode(&menustate->device->device(), TRUE))
+ mode_flags |= MENU_FLAG_RIGHT_ARROW;
+
+ if (bitbanger_dec_mode(&menustate->device->device(), TRUE))
+ mode_flags |= MENU_FLAG_LEFT_ARROW;
+
+ if (bitbanger_inc_baud(&menustate->device->device(), TRUE))
+ baud_flags |= MENU_FLAG_RIGHT_ARROW;
+
+ if (bitbanger_dec_baud(&menustate->device->device(), TRUE))
+ baud_flags |= MENU_FLAG_LEFT_ARROW;
+
+ if (bitbanger_inc_tune(&menustate->device->device(), TRUE))
+ tune_flags |= MENU_FLAG_RIGHT_ARROW;
+
+ if (bitbanger_dec_tune(&menustate->device->device(), TRUE))
+ tune_flags |= MENU_FLAG_LEFT_ARROW;
+
+
+ if (menustate->device->exists())
+ {
+ /* name of bitbanger file */
+ ui_menu_item_append(menu, menustate->device->image_config().devconfig().name(), menustate->device->filename(), flags, BITBANGERCMD_SELECT);
+ ui_menu_item_append(menu, "Device Mode:", bitbanger_mode_string(&menustate->device->device()), mode_flags, BITBANGERCMD_MODE);
+ ui_menu_item_append(menu, "Baud:", bitbanger_baud_string(&menustate->device->device()), baud_flags, BITBANGERCMD_BAUD);
+ ui_menu_item_append(menu, "Baud Tune:", bitbanger_tune_string(&menustate->device->device()), tune_flags, BITBANGERCMD_TUNE);
+ ui_menu_item_append(menu, "Protocol:", "8-1-N", 0, NULL);
+ }
+ else
+ {
+ /* no tape loaded */
+ ui_menu_item_append(menu, "No Bitbanger Image loaded", NULL, flags, NULL);
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_tape_control - main tape control menu
+-------------------------------------------------*/
+
+void ui_mess_menu_tape_control(running_machine *machine, ui_menu *menu, void *parameter, void *state)
+{
+ tape_control_menu_state *menustate;
+ const ui_menu_event *event;
+
+ /* if no state, allocate some */
+ if (state == NULL)
+ state = ui_menu_alloc_state(menu, sizeof(*menustate), NULL);
+ menustate = (tape_control_menu_state *) state;
+
+ /* do we have to load the device? */
+ if (menustate->device == NULL)
+ {
+ int index = menustate->index;
+ device_image_interface *device = NULL;
+ for (bool gotone = machine->m_devicelist.first(device); gotone; gotone = device->next(device))
+ {
+ if(device->device().type() == CASSETTE) {
+ if (index==0) break;
+ index--;
+ }
+ }
+ menustate->device = device;
+ ui_menu_reset(menu, (ui_menu_reset_options)0);
+ }
+
+ /* rebuild the menu - we have to do this so that the counter updates */
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION);
+ menu_tape_control_populate(machine, menu, (tape_control_menu_state*)state);
+
+ /* process the menu */
+ event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT);
+ if (event != NULL)
+ {
+ switch(event->iptkey)
+ {
+ case IPT_UI_LEFT:
+ if (event->itemref==TAPECMD_SLIDER)
+ cassette_seek(&menustate->device->device(), -1, SEEK_CUR);
+ else
+ if (event->itemref==TAPECMD_SELECT)
+ {
+ /* left arrow - rotate left through cassette devices */
+ if (menustate->index > 0)
+ menustate->index--;
+ else
+ menustate->index = cassette_count(machine) - 1;
+ menustate->device = NULL;
+ }
+ break;
+
+ case IPT_UI_RIGHT:
+ if (event->itemref==TAPECMD_SLIDER)
+ cassette_seek(&menustate->device->device(), +1, SEEK_CUR);
+ else
+ if (event->itemref==TAPECMD_SELECT)
+ {
+ /* right arrow - rotate right through cassette devices */
+ if (menustate->index < cassette_count(machine) - 1)
+ menustate->index++;
+ else
+ menustate->index = 0;
+ menustate->device = NULL;
+ }
+ break;
+
+ case IPT_UI_SELECT:
+ {
+ if (event->itemref==TAPECMD_STOP)
+ cassette_change_state(&menustate->device->device(), CASSETTE_STOPPED, CASSETTE_MASK_UISTATE);
+ else
+ if (event->itemref==TAPECMD_PLAY)
+ cassette_change_state(&menustate->device->device(), CASSETTE_PLAY, CASSETTE_MASK_UISTATE);
+ else
+ if (event->itemref==TAPECMD_RECORD)
+ cassette_change_state(&menustate->device->device(), CASSETTE_RECORD, CASSETTE_MASK_UISTATE);
+ else
+ if (event->itemref==TAPECMD_REWIND)
+ cassette_seek(&menustate->device->device(), -30, SEEK_CUR);
+ else
+ if (event->itemref==TAPECMD_FAST_FORWARD)
+ cassette_seek(&menustate->device->device(), 30, SEEK_CUR);
+ else
+ if (event->itemref==TAPECMD_SLIDER)
+ cassette_seek(&menustate->device->device(), 0, SEEK_SET);
+ }
+ break;
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_bitbanger_control - main bitbanger
+ control menu
+-------------------------------------------------*/
+
+void ui_mess_menu_bitbanger_control(running_machine *machine, ui_menu *menu, void *parameter, void *state)
+{
+ bitbanger_control_menu_state *menustate;
+ const ui_menu_event *event;
+
+ /* if no state, allocate some */
+ if (state == NULL)
+ state = ui_menu_alloc_state(menu, sizeof(*menustate), NULL);
+ menustate = (bitbanger_control_menu_state *) state;
+
+ /* do we have to load the device? */
+ if (menustate->device == NULL)
+ {
+ int index = menustate->index;
+ device_image_interface *device = NULL;
+ for (bool gotone = machine->m_devicelist.first(device); gotone; gotone = device->next(device))
+ {
+ if(device->device().type() == BITBANGER) {
+ if (index==0) break;
+ index--;
+ }
+ }
+ menustate->device = device;
+ ui_menu_reset(menu, (ui_menu_reset_options)0);
+ }
+
+ /* rebuild the menu */
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION);
+ menu_bitbanger_control_populate(machine, menu, (bitbanger_control_menu_state*)state);
+
+ /* process the menu */
+ event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT);
+ if (event != NULL)
+ {
+ switch(event->iptkey)
+ {
+ case IPT_UI_LEFT:
+ if (event->itemref==BITBANGERCMD_SELECT)
+ {
+ /* left arrow - rotate left through cassette devices */
+ if (menustate->index > 0)
+ menustate->index--;
+ else
+ menustate->index = bitbanger_count(machine) - 1;
+ menustate->device = NULL;
+ }
+ else if (event->itemref==BITBANGERCMD_MODE)
+ {
+ bitbanger_dec_mode(&menustate->device->device(), FALSE);
+ }
+ else if (event->itemref==BITBANGERCMD_BAUD)
+ {
+ bitbanger_dec_baud(&menustate->device->device(), FALSE);
+ }
+ else if (event->itemref==BITBANGERCMD_TUNE)
+ {
+ bitbanger_dec_tune(&menustate->device->device(), FALSE);
+ }
+ break;
+
+ case IPT_UI_RIGHT:
+ if (event->itemref==BITBANGERCMD_SELECT)
+ {
+ /* right arrow - rotate right through cassette devices */
+ if (menustate->index < bitbanger_count(machine) - 1)
+ menustate->index++;
+ else
+ menustate->index = 0;
+ menustate->device = NULL;
+ }
+ else if (event->itemref==BITBANGERCMD_MODE)
+ {
+ bitbanger_inc_mode(&menustate->device->device(), FALSE);
+ }
+ else if (event->itemref==BITBANGERCMD_BAUD)
+ {
+ bitbanger_inc_baud(&menustate->device->device(), FALSE);
+ }
+ else if (event->itemref==BITBANGERCMD_TUNE)
+ {
+ bitbanger_inc_tune(&menustate->device->device(), FALSE);
+ }
+ break;
+ }
+ }
+}
+
diff --git a/src/emu/uiimage.h b/src/emu/uiimage.h
index 25c08636058..5c83d64e44e 100644
--- a/src/emu/uiimage.h
+++ b/src/emu/uiimage.h
@@ -24,4 +24,8 @@ void ui_image_menu_file_manager(running_machine *machine, ui_menu *menu, void *p
void ui_image_menu_image_info(running_machine *machine, ui_menu *menu, void *parameter, void *state);
+void ui_mess_menu_bitbanger_control(running_machine *machine, ui_menu *menu, void *parameter, void *state);
+
+void ui_mess_menu_tape_control(running_machine *machine, ui_menu *menu, void *parameter, void *state);
+
#endif /* __UIIMAGE_H__ */
diff --git a/src/emu/uimenu.c b/src/emu/uimenu.c
index 14c5fcf4138..339e3128bfc 100644
--- a/src/emu/uimenu.c
+++ b/src/emu/uimenu.c
@@ -19,12 +19,9 @@
#include "uimenu.h"
#include "audit.h"
#include "crsshair.h"
-
-#ifdef MESS
-#include "uimess.h"
-#endif /* MESS */
-
#include <ctype.h>
+#include "imagedev/cassette.h"
+#include "imagedev/bitbngr.h"
@@ -1549,10 +1546,14 @@ static void menu_main_populate(running_machine *machine, ui_menu *menu, void *st
/* add file manager menu */
ui_menu_item_append(menu, "File Manager", NULL, 0, (void*)ui_image_menu_file_manager);
- #ifdef MESS
- /* add MESS-specific menus */
- ui_mess_main_menu_populate(machine, menu);
- #endif /* MESS */
+
+ /* add tape control menu */
+ if (machine->m_devicelist.first(CASSETTE))
+ ui_menu_item_append(menu, "Tape Control", NULL, 0, (void*)ui_mess_menu_tape_control);
+
+ /* add bitbanger control menu */
+ if (machine->m_devicelist.first(BITBANGER))
+ ui_menu_item_append(menu, "Bitbanger Control", NULL, 0, (void*)ui_mess_menu_bitbanger_control);
}
/* add keyboard mode menu */
if (input_machine_has_keyboard(machine) && inputx_can_post(machine))
diff --git a/src/mame/drivers/megadriv.c b/src/mame/drivers/megadriv.c
index 20a0dae0985..520a878ef7f 100644
--- a/src/mame/drivers/megadriv.c
+++ b/src/mame/drivers/megadriv.c
@@ -271,9 +271,7 @@ a tilemap-like structure, from which data is copied)
#include "cpu/sh2/sh2.h"
#include "cpu/sh2/sh2comn.h"
#include "sound/cdda.h"
-#ifdef MESS
-#include "devices/chd_cd.h"
-#endif
+#include "imagedev/chd_cd.h"
#include "sound/rf5c68.h"
#define MEGADRIV_VDP_VRAM(address) megadrive_vdp_vram[(address)&0x7fff]
diff --git a/src/mame/machine/stvcd.c b/src/mame/machine/stvcd.c
index 62ce635ea0c..09467532666 100644
--- a/src/mame/machine/stvcd.c
+++ b/src/mame/machine/stvcd.c
@@ -22,9 +22,7 @@
***************************************************************************/
#include "emu.h"
-#ifdef MESS
-#include "devices/chd_cd.h"
-#endif
+#include "imagedev/chd_cd.h"
#include "cdrom.h"
#include "stvcd.h"
diff --git a/src/osd/windows/input.c b/src/osd/windows/input.c
index a2cd7b66567..dad66fda7fd 100644
--- a/src/osd/windows/input.c
+++ b/src/osd/windows/input.c
@@ -72,12 +72,6 @@
#include "strconv.h"
#include "config.h"
-#ifdef MESS
-#include "uimess.h"
-#endif
-
-
-
//============================================================
// PARAMETERS
//============================================================