diff options
Diffstat (limited to 'src')
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, §or_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, §or, §or_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, §or_length, NULL); + else + err = fmt->get_sector_length(floppy, head, track, sector, §or_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 //============================================================ |