/************************************************************************* * * * Copyright (c) 2007-2022 Howard M. Harte. * * https://github.com/hharte * * * * Permission is hereby granted, free of charge, to any person obtaining * * a copy of this software and associated documentation files (the * * "Software"), to deal in the Software without restriction, including * * without limitation the rights to use, copy, modify, merge, publish, * * distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to * * the following conditions: * * * * The above copyright notice and this permission notice shall be * * included in all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON- * * INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * * SOFTWARE. * * * * Except as contained in this notice, the names of The Authors shall * * not be used in advertising or otherwise to promote the sale, use or * * other dealings in this Software without prior written authorization * * from the Authors. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * CompuPro M-DRIVE/H Controller module for SIMH. * * * *************************************************************************/ /*#define DBG_MSG */ #include "altairz80_defs.h" #ifdef DBG_MSG #define DBG_PRINT(args) sim_printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define SEEK_MSG (1 << 0) #define RD_DATA_MSG (1 << 1) #define WR_DATA_MSG (1 << 2) #define VERBOSE_MSG (1 << 3) #define MDRIVEH_MAX_DRIVES 8 typedef struct { PNP_INFO pnp; /* Plug and Play */ uint32 dma_addr; /* DMA Transfer Address */ UNIT uptr[MDRIVEH_MAX_DRIVES]; uint8 *storage[MDRIVEH_MAX_DRIVES]; } MDRIVEH_INFO; static MDRIVEH_INFO mdriveh_info_data = { { 0x0, 0, 0xC6, 2 } }; static MDRIVEH_INFO *mdriveh_info = &mdriveh_info_data; extern uint32 PCX; extern t_stat set_iobase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), const char* name, uint8 unmap); #define UNIT_V_MDRIVEH_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_MDRIVEH_WLK (1 << UNIT_V_MDRIVEH_WLK) #define UNIT_V_MDRIVEH_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_MDRIVEH_VERBOSE (1 << UNIT_V_MDRIVEH_VERBOSE) #define MDRIVEH_CAPACITY (512 * 1000) /* Default M-DRIVE/H Capacity */ #define MDRIVEH_NONE 0 static t_stat mdriveh_reset(DEVICE *mdriveh_dev); static int32 mdrivehdev(const int32 port, const int32 io, const int32 data); static uint8 MDRIVEH_Read(const uint32 Addr); static uint8 MDRIVEH_Write(const uint32 Addr, uint8 cData); static const char* mdriveh_description(DEVICE *dptr); static UNIT mdriveh_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) } }; static REG mdriveh_reg[] = { { NULL } }; #define MDRIVEH_NAME "Compupro Memory Drive" static const char* mdriveh_description(DEVICE *dptr) { return MDRIVEH_NAME; } static MTAB mdriveh_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL, "Sets disk controller I/O base address" }, { UNIT_MDRIVEH_WLK, 0, "WRTENB", "WRTENB", NULL, NULL, NULL, "Enables " MDRIVEH_NAME "n for writing" }, { UNIT_MDRIVEH_WLK, UNIT_MDRIVEH_WLK, "WRTLCK", "WRTLCK", NULL, NULL, NULL, "Locks " MDRIVEH_NAME "n for writing" }, /* quiet, no warning messages */ { UNIT_MDRIVEH_VERBOSE, 0, "QUIET", "QUIET", NULL, NULL, NULL, "No verbose messages for unit " MDRIVEH_NAME "n" }, /* verbose, show warning messages */ { UNIT_MDRIVEH_VERBOSE, UNIT_MDRIVEH_VERBOSE, "VERBOSE", "VERBOSE", NULL, NULL, NULL, "Verbose messages for unit " MDRIVEH_NAME "n" }, { 0 } }; /* Debug Flags */ static DEBTAB mdriveh_dt[] = { { "SEEK", SEEK_MSG, "Seek messages" }, { "READ", RD_DATA_MSG, "Read messages" }, { "WRITE", WR_DATA_MSG, "Write messages" }, { "VERBOSE", VERBOSE_MSG, "Verbose messages" }, { NULL, 0 } }; DEVICE mdriveh_dev = { "MDRIVEH", mdriveh_unit, mdriveh_reg, mdriveh_mod, MDRIVEH_MAX_DRIVES, 10, 31, 1, MDRIVEH_MAX_DRIVES, MDRIVEH_MAX_DRIVES, NULL, NULL, &mdriveh_reset, NULL, NULL, NULL, &mdriveh_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), 0, mdriveh_dt, NULL, NULL, NULL, NULL, NULL, &mdriveh_description }; /* Reset routine */ static t_stat mdriveh_reset(DEVICE *dptr) { uint8 i; PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect ROM and I/O Ports */ sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &mdrivehdev, "mdrivehdev", TRUE); } else { /* Connect MDRIVEH at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &mdrivehdev, "mdrivehdev", FALSE) != 0) { sim_printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } } for(i=0; iuptr[i] = dptr->units[i]; if((dptr->flags & DEV_DIS) || (dptr->units[i].flags & UNIT_DIS)) { if (dptr->units[i].flags & UNIT_MDRIVEH_VERBOSE) sim_printf("MDRIVEH: Unit %d disabled", i); if(mdriveh_info->storage[i] != NULL) { if (dptr->units[i].flags & UNIT_MDRIVEH_VERBOSE) sim_printf(", freed 0x%p\n", mdriveh_info->storage[i]); free(mdriveh_info->storage[i]); mdriveh_info->storage[i] = NULL; } else if (dptr->units[i].flags & UNIT_MDRIVEH_VERBOSE) { sim_printf(".\n"); } } else { if(mdriveh_info->storage[i] == NULL) { mdriveh_info->storage[i] = (uint8 *)calloc(1, 524288); } if (dptr->units[i].flags & UNIT_MDRIVEH_VERBOSE) sim_printf("MDRIVEH: Unit %d enabled, 512K at 0x%p\n", i, mdriveh_info->storage[i]); } } return SCPE_OK; } static int32 mdrivehdev(const int32 port, const int32 io, const int32 data) { DBG_PRINT(("MDRIVEH: " ADDRESS_FORMAT " IO %s, Port %02x\n", PCX, io ? "WR" : "RD", port)); if(io) { MDRIVEH_Write(port, data); return 0; } else { return(MDRIVEH_Read(port)); } } #define MDRIVEH_DATA 0 /* R=Drive Status Register / W=DMA Address Register */ #define MDRIVEH_ADDR 1 /* R=Unused / W=Motor Control Register */ static uint8 MDRIVEH_Read(const uint32 Addr) { uint8 cData; uint8 unit; uint32 offset; cData = 0xFF; /* default is High-Z Data */ switch(Addr & 0x1) { case MDRIVEH_ADDR: sim_debug(VERBOSE_MSG, &mdriveh_dev, "MDRIVEH: " ADDRESS_FORMAT " RD Addr = 0x%02x\n", PCX, cData); break; case MDRIVEH_DATA: unit = (mdriveh_info->dma_addr & 0x380000) >> 19; offset = mdriveh_info->dma_addr & 0x7FFFF; if(mdriveh_info->storage[unit] != NULL) { cData = mdriveh_info->storage[unit][offset]; } sim_debug(RD_DATA_MSG, &mdriveh_dev, "MDRIVEH: " ADDRESS_FORMAT " RD Data [%x:%05x] = 0x%02x\n", PCX, unit, offset, cData); /* Increment M-DRIVE/H Data Address */ mdriveh_info->dma_addr++; mdriveh_info->dma_addr &= 0x3FFFFF; break; } return (cData); } static uint8 MDRIVEH_Write(const uint32 Addr, uint8 cData) { uint8 result = 0; uint8 unit; uint32 offset; switch(Addr & 0x1) { case MDRIVEH_ADDR: mdriveh_info->dma_addr <<= 8; mdriveh_info->dma_addr &= 0x003FFF00; mdriveh_info->dma_addr |= cData; sim_debug(SEEK_MSG, &mdriveh_dev, "MDRIVEH: " ADDRESS_FORMAT " DMA Address=%06x\n", PCX, mdriveh_info->dma_addr); break; case MDRIVEH_DATA: unit = (mdriveh_info->dma_addr & 0x380000) >> 19; offset = mdriveh_info->dma_addr & 0x7FFFF; if(mdriveh_info->storage[unit] != NULL) { if(mdriveh_info->uptr[unit].flags & UNIT_MDRIVEH_WLK) { sim_debug(WR_DATA_MSG, &mdriveh_dev, "MDRIVEH: " ADDRESS_FORMAT " WR Data [%x:%05x] = Unit Write Locked\n", PCX, unit, offset); } else { sim_debug(WR_DATA_MSG, &mdriveh_dev, "MDRIVEH: " ADDRESS_FORMAT " WR Data [%x:%05x] = 0x%02x\n", PCX, unit, offset, cData); mdriveh_info->storage[unit][offset] = cData; } } else { sim_debug(WR_DATA_MSG, &mdriveh_dev, "MDRIVEH: " ADDRESS_FORMAT " WR Data [%x:%05x] = Unit OFFLINE\n", PCX, unit, offset); } /* Increment M-DRIVE/H Data Address */ mdriveh_info->dma_addr++; mdriveh_info->dma_addr &= 0x3FFFFF; break; } return (result); }