// license:BSD-3-Clause // copyright-holders:R. Belmont /*************************************************************************** nubus_image.c - synthetic NuBus card to allow reading/writing "raw" HFS images, including floppy images (DD and HD) and vMac/Basilisk HDD volumes up to 256 MB in size. ***************************************************************************/ #include "emu.h" #include "nubus_image.h" #include "osdcore.h" #define IMAGE_ROM_REGION "image_rom" #define IMAGE_DISK0_TAG "nb_disk" #define MESSIMG_DISK_SECTOR_SIZE (512) // on big-endian, these are NOPs. (TODO: hey, where did WORDS_BIGENDIAN go since the GENie transition?!) #if defined(__ppc__) || defined (__PPC__) || defined(__ppc64__) || defined(__PPC64__) static UINT32 ni_htonl(UINT32 x) { return x; } static UINT32 ni_ntohl(UINT32 x) { return x; } #else static UINT32 ni_htonl(UINT32 x) { return FLIPENDIAN_INT32(x); } static UINT32 ni_ntohl(UINT32 x) { return FLIPENDIAN_INT32(x); } #endif // messimg_disk_image_device class messimg_disk_image_device : public device_t, public device_image_interface { public: // construction/destruction messimg_disk_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); // image-level overrides virtual iodevice_t image_type() const override { return IO_QUICKLOAD; } virtual bool is_readable() const override { return 1; } virtual bool is_writeable() const override { return 1; } virtual bool is_creatable() const override { return 0; } virtual bool must_be_loaded() const override { return 0; } virtual bool is_reset_on_load() const override { return 0; } virtual const char *image_interface() const override { return nullptr; } virtual const char *file_extensions() const override { return "img"; } virtual const option_guide *create_option_guide() const override { return nullptr; } virtual bool call_load() override; virtual void call_unload() override; protected: // device-level overrides virtual void device_config_complete() override; virtual void device_start() override; virtual void device_reset() override; public: UINT32 m_size; std::unique_ptr m_data; bool m_ejected; }; // device type definition extern const device_type MESSIMG_DISK; const device_type MESSIMG_DISK = &device_creator; messimg_disk_image_device::messimg_disk_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, MESSIMG_DISK, "Mac image", tag, owner, clock, "messimg_disk_image", __FILE__), device_image_interface(mconfig, *this), m_size(0), m_data(nullptr), m_ejected(false) { } void messimg_disk_image_device::device_config_complete() { update_names(MESSIMG_DISK, "disk", "disk"); } /*------------------------------------------------- device start callback -------------------------------------------------*/ void messimg_disk_image_device::device_start() { m_data = nullptr; if (exists() && fseek(0, SEEK_END) == 0) { m_size = (UINT32)ftell(); } } bool messimg_disk_image_device::call_load() { fseek(0, SEEK_END); m_size = (UINT32)ftell(); if (m_size > (256*1024*1024)) { printf("Mac image too large: must be 256MB or less!\n"); m_size = 0; return IMAGE_INIT_FAIL; } m_data = make_unique_clear(m_size); fseek(0, SEEK_SET); fread(m_data.get(), m_size); m_ejected = false; return IMAGE_INIT_PASS; } void messimg_disk_image_device::call_unload() { // TODO: track dirty sectors and only write those fseek(0, SEEK_SET); fwrite(m_data.get(), m_size); m_size = 0; //free(m_data); } /*------------------------------------------------- device reset callback -------------------------------------------------*/ void messimg_disk_image_device::device_reset() { } MACHINE_CONFIG_FRAGMENT( image ) MCFG_DEVICE_ADD(IMAGE_DISK0_TAG, MESSIMG_DISK, 0) MACHINE_CONFIG_END ROM_START( image ) ROM_REGION(0x2000, IMAGE_ROM_REGION, 0) ROM_LOAD( "nb_fake.bin", 0x000000, 0x002000, CRC(9264bac5) SHA1(540c2ce3c90382b2da6e1e21182cdf8fc3f0c930) ) ROM_END //************************************************************************** // GLOBAL VARIABLES //************************************************************************** const device_type NUBUS_IMAGE = &device_creator; //------------------------------------------------- // machine_config_additions - device-specific // machine configurations //------------------------------------------------- machine_config_constructor nubus_image_device::device_mconfig_additions() const { return MACHINE_CONFIG_NAME( image ); } //------------------------------------------------- // rom_region - device-specific ROM region //------------------------------------------------- const rom_entry *nubus_image_device::device_rom_region() const { return ROM_NAME( image ); } //************************************************************************** // LIVE DEVICE //************************************************************************** //------------------------------------------------- // nubus_image_device - constructor //------------------------------------------------- nubus_image_device::nubus_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, NUBUS_IMAGE, "Disk Image Pseudo-Card", tag, owner, clock, "nb_image", __FILE__), device_nubus_card_interface(mconfig, *this), m_image(nullptr) { } nubus_image_device::nubus_image_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) : device_t(mconfig, type, name, tag, owner, clock, shortname, source), device_nubus_card_interface(mconfig, *this), m_image(nullptr) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void nubus_image_device::device_start() { UINT32 slotspace; UINT32 superslotspace; // set_nubus_device makes m_slot valid set_nubus_device(); install_declaration_rom(this, IMAGE_ROM_REGION); slotspace = get_slotspace(); superslotspace = get_super_slotspace(); // printf("[image %p] slotspace = %x, super = %x\n", this, slotspace, superslotspace); m_nubus->install_device(slotspace, slotspace+3, read32_delegate(FUNC(nubus_image_device::image_r), this), write32_delegate(FUNC(nubus_image_device::image_w), this)); m_nubus->install_device(slotspace+4, slotspace+7, read32_delegate(FUNC(nubus_image_device::image_status_r), this), write32_delegate(FUNC(nubus_image_device::image_status_w), this)); m_nubus->install_device(slotspace+8, slotspace+11, read32_delegate(FUNC(nubus_image_device::file_cmd_r), this), write32_delegate(FUNC(nubus_image_device::file_cmd_w), this)); m_nubus->install_device(slotspace+12, slotspace+15, read32_delegate(FUNC(nubus_image_device::file_data_r), this), write32_delegate(FUNC(nubus_image_device::file_data_w), this)); m_nubus->install_device(slotspace+16, slotspace+19, read32_delegate(FUNC(nubus_image_device::file_len_r), this), write32_delegate(FUNC(nubus_image_device::file_len_w), this)); m_nubus->install_device(slotspace+20, slotspace+147, read32_delegate(FUNC(nubus_image_device::file_name_r), this), write32_delegate(FUNC(nubus_image_device::file_name_w), this)); m_nubus->install_device(superslotspace, superslotspace+((256*1024*1024)-1), read32_delegate(FUNC(nubus_image_device::image_super_r), this), write32_delegate(FUNC(nubus_image_device::image_super_w), this)); m_image = subdevice(IMAGE_DISK0_TAG); filectx.curdir[0] = '.'; filectx.curdir[1] = '\0'; filectx.dirp = nullptr; filectx.fd = nullptr; } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void nubus_image_device::device_reset() { } WRITE32_MEMBER( nubus_image_device::image_status_w ) { m_image->m_ejected = true; } READ32_MEMBER( nubus_image_device::image_status_r ) { if(m_image->m_ejected) { return 0; } if(m_image->m_size) { return 1; } return 0; } WRITE32_MEMBER( nubus_image_device::image_w ) { } READ32_MEMBER( nubus_image_device::image_r ) { return m_image->m_size; } WRITE32_MEMBER( nubus_image_device::image_super_w ) { UINT32 *image = (UINT32*)m_image->m_data.get(); data = ((data & 0xff) << 24) | ((data & 0xff00) << 8) | ((data & 0xff0000) >> 8) | ((data & 0xff000000) >> 24); mem_mask = ((mem_mask & 0xff) << 24) | ((mem_mask & 0xff00) << 8) | ((mem_mask & 0xff0000) >> 8) | ((mem_mask & 0xff000000) >> 24); COMBINE_DATA(&image[offset]); } READ32_MEMBER( nubus_image_device::image_super_r ) { UINT32 *image = (UINT32*)m_image->m_data.get(); UINT32 data = image[offset]; return ((data & 0xff) << 24) | ((data & 0xff00) << 8) | ((data & 0xff0000) >> 8) | ((data & 0xff000000) >> 24); } WRITE32_MEMBER( nubus_image_device::file_cmd_w ) { const osd::directory::entry *dp; char fullpath[1024]; UINT64 filesize; // data = ((data & 0xff) << 24) | ((data & 0xff00) << 8) | ((data & 0xff0000) >> 8) | ((data & 0xff000000) >> 24); filectx.curcmd = data; switch(data) { case kFileCmdGetDir: strcpy((char*)filectx.filename, (char*)filectx.curdir); break; case kFileCmdSetDir: if ((filectx.filename[0] == '/') || (filectx.filename[0] == '$')) { strcpy((char*)filectx.curdir, (char*)filectx.filename); } else { strcat((char*)filectx.curdir, "/"); strcat((char*)filectx.curdir, (char*)filectx.filename); } break; case kFileCmdGetFirstListing: filectx.dirp = osd::directory::open((const char *)filectx.curdir); case kFileCmdGetNextListing: if (filectx.dirp) { dp = filectx.dirp->read(); if(dp) { strncpy((char*)filectx.filename, dp->name, sizeof(filectx.filename)); } else { memset(filectx.filename, 0, sizeof(filectx.filename)); } } else { memset(filectx.filename, 0, sizeof(filectx.filename)); } break; case kFileCmdGetFile: memset(fullpath, 0, sizeof(fullpath)); strcpy(fullpath, (const char *)filectx.curdir); strcat(fullpath, "/"); strcat(fullpath, (const char*)filectx.filename); if(osd_file::open(std::string(fullpath), OPEN_FLAG_READ, filectx.fd, filectx.filelen) != osd_file::error::NONE) printf("Error opening %s\n", fullpath); filectx.bytecount = 0; break; case kFileCmdPutFile: memset(fullpath, 0, sizeof(fullpath)); strcpy(fullpath, (const char *)filectx.curdir); strcat(fullpath, "/"); strcat(fullpath, (const char*)filectx.filename); if(osd_file::open(std::string(fullpath), OPEN_FLAG_WRITE|OPEN_FLAG_CREATE, filectx.fd, filesize) != osd_file::error::NONE) printf("Error opening %s\n", fullpath); filectx.bytecount = 0; break; } } READ32_MEMBER( nubus_image_device::file_cmd_r ) { return 0; } WRITE32_MEMBER( nubus_image_device::file_data_w ) { std::uint32_t count = 4; std::uint32_t actualcount = 0; data = ((data & 0xff) << 24) | ((data & 0xff00) << 8) | ((data & 0xff0000) >> 8) | ((data & 0xff000000) >> 24); if(filectx.fd) { //data = ni_ntohl(data); if((filectx.bytecount + count) > filectx.filelen) count = filectx.filelen - filectx.bytecount; filectx.fd->write(&data, filectx.bytecount, count, actualcount); filectx.bytecount += actualcount; if(filectx.bytecount >= filectx.filelen) { filectx.fd.reset(); } } } READ32_MEMBER( nubus_image_device::file_data_r ) { if(filectx.fd) { std::uint32_t ret; std::uint32_t actual = 0; filectx.fd->read(&ret, filectx.bytecount, sizeof(ret), actual); filectx.bytecount += actual; if(actual < sizeof(ret)) { filectx.fd.reset(); } return ni_htonl(ret); } return 0; } WRITE32_MEMBER( nubus_image_device::file_len_w ) { data = ((data & 0xff) << 24) | ((data & 0xff00) << 8) | ((data & 0xff0000) >> 8) | ((data & 0xff000000) >> 24); filectx.filelen = ni_ntohl(data); } READ32_MEMBER( nubus_image_device::file_len_r ) { return filectx.filelen; } WRITE32_MEMBER( nubus_image_device::file_name_w ) { ((UINT32*)(filectx.filename))[offset] = ni_ntohl(data); } READ32_MEMBER( nubus_image_device::file_name_r ) { UINT32 ret; ret = ni_htonl(((UINT32*)(filectx.filename))[offset]); return ret; }