simh-testsetgenerator/AltairZ80/s100_djhdc.c
2023-06-30 14:31:06 +02:00

864 lines
33 KiB
C

/*************************************************************************
* *
* Copyright (c) 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. *
* *
* Based on s100_disk3.c *
* *
* Module Description: *
* Morrow Disk Jockey HDC-DMA Hard Disk Controller module for SIMH. *
* Reference: *
* http://www.bitsavers.org/pdf/morrow/boards/HDC_DMA_Technical_Manual_1983.pdf *
* *
*************************************************************************/
#include "altairz80_defs.h"
#include "sim_imd.h"
#define DEV_NAME "DJHDC"
#define DJHDC_MAX_CYLS 1024
#define DJHDC_MAX_HEADS 8
#define DJHDC_MAX_SPT 256
/* Debug flags */
#define ERROR_MSG (1 << 0)
#define SEEK_MSG (1 << 1)
#define OPCODE_MSG (1 << 2)
#define RD_DATA_MSG (1 << 3)
#define WR_DATA_MSG (1 << 4)
#define IRQ_MSG (1 << 5)
#define VERBOSE_MSG (1 << 6)
#define FORMAT_MSG (1 << 7)
#define DJHDC_MAX_DRIVES 4
/* DJHDC I/O Ports */
#define DJHDC_RESET 0 /* Reset */
#define DJHDC_START 1 /* Start */
#define DJHDC_LINK_ADDR 0x000050 /* Default link address in RAM */
#define DJHDC_STEP_DIR 0x10 /* Bit 4, Step OUT */
#define DJHDC_IRQ_EN_MASK 0x80 /* Interrupt enable mask */
#define DJHDC_OPCODE_READ_DATA 0x00
#define DJHDC_OPCODE_WRITE_DATA 0x01
#define DJHDC_OPCODE_READ_HEADER 0x02
#define DJHDC_OPCODE_FORMAT_TRACK 0x03
#define DJHDC_OPCODE_LOAD_CONSTANTS 0x04
#define DJHDC_OPCODE_SENSE_STATUS 0x05
#define DJHDC_OPCODE_NOOP 0x06
#define DJHDC_STATUS_BUSY 0x00 /* Busy */
#define DJHDC_STATUS_NOT_READY 0x01 /* Drive not ready */
#define DJHDC_STATUS_HEADER_NOT_FOUND 0x04 /* Sector header not found */
#define DJHDC_STATUS_DATA_NOT_FOUND 0x05 /* Sector data not found */
#define DJHDC_STATUS_DATA_OVERRUN 0x06 /* Data overrun(channel error) */
#define DJHDC_STATUS_DATA_CRC_ERROR 0x07 /* Data CRC error */
#define DJHDC_STATUS_WRITE_FAULT 0x08 /* Write fault */
#define DJHDC_STATUS_HEADER_CRC_ERROR 0x09 /* Sector header CRC error */
#define DJHDC_STATUS_ILLEGAL_COMMAND 0xA0 /* Illegal command */
#define DJHDC_STATUS_COMPLETE 0xFF /* Successful completion */
#define DJHDC_TRACK_0_DETECT (1 << 0) /* 0 = track 0. */
#define DJHDC_WRITE_FAULT_SIGNAL (1 << 1) /* Drive Write fault (0). */
#define DJHDC_DRIVE_READY_SIGNAL (1 << 2) /* Drive is up to speed (0). */
#define DJHDC_OPCODE_MASK 0x07
#define DJHDC_IOPB_LEN 16
#define DJHDC_IOPB_SELDRV 0
#define DJHDC_IOPB_STEP_L 1
#define DJHDC_IOPB_STEP_H 2
#define DJHDC_IOPB_SEL_HD 3
#define DJHDC_IOPB_DMA_L 4
#define DJHDC_IOPB_DMA_H 5
#define DJHDC_IOPB_DMA_E 6
#define DJHDC_IOPB_ARG0 7
#define DJHDC_IOPB_ARG1 8
#define DJHDC_IOPB_ARG2 9
#define DJHDC_IOPB_ARG3 10
#define DJHDC_IOPB_OPCODE 11
#define DJHDC_IOPB_STATUS 12
#define DJHDC_IOPB_LINK 13
#define DJHDC_IOPB_LINK_H 14
#define DJHDC_IOPB_LINK_E 15
#define DJHDC_INT 1 /* DJHDC interrupts tied to VI1 */
typedef struct {
UNIT *uptr;
DISK_INFO *imd;
uint16 sectsize; /* sector size, not including pre/postamble */
uint16 nsectors; /* number of sectors/track */
uint16 nheads; /* number of heads */
uint16 ntracks; /* number of tracks */
uint16 res_tracks; /* Number of reserved tracks on drive. */
uint16 track; /* Current Track */
uint16 cur_sect; /* current starting sector of transfer */
uint16 cur_cyl; /* Current Track */
uint16 cur_head; /* Number of sectors to transfer */
uint16 cur_sectsize;/* Current sector size */
uint8 ready; /* Is drive ready? */
} DJHDC_DRIVE_INFO;
typedef struct {
PNP_INFO pnp; /* Plug and Play */
uint8 sel_drive; /* Currently selected drive */
uint8 mode; /* mode (0xFF=absolute, 0x00=logical) */
uint8 ndrives; /* Number of drives attached to the controller */
uint32 link_addr; /* Link Address for next IOPB */
uint32 dma_addr; /* DMA Address for the current IOPB */
uint16 steps; /* Step count */
uint8 step_dir; /* Step direction, 1 = out. */
uint8 irq_enable;
uint8 step_delay;
uint8 head_settle_time;
uint8 sector_size_code;
DJHDC_DRIVE_INFO drive[DJHDC_MAX_DRIVES];
uint8 iopb[16];
} DJHDC_INFO;
static DJHDC_INFO djhdc_info_data = { { 0x0, 0, 0x54, 2 } };
static DJHDC_INFO *djhdc_info = &djhdc_info_data;
/* Disk geometries:
* IMI SCRIBE
* Sectsize: 1024 1024
* Sectors: 8 8
* Heads: 6 4
* Tracks: 306 480
*/
/* Default geometry for a 15MB hard disk. */
#define SCRIBE_SECTSIZE 1024
#define SCRIBE_NSECTORS 8
#define SCRIBE_NHEADS 4
#define SCRIBE_NTRACKS 480
static const char* djhdc_opcode_str[] = {
"Read Data ",
"Write Data ",
"Read Header ",
"Format Track ",
"Load Constants",
"Sense Status ",
"No Operation ",
"Invalid "
};
static int32 ntracks = SCRIBE_NTRACKS;
static int32 nheads = SCRIBE_NHEADS;
static int32 nsectors = SCRIBE_NSECTORS;
static int32 sectsize = SCRIBE_SECTSIZE;
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);
extern int32 find_unit_index(UNIT *uptr);
extern void raise_scp300f_interrupt(uint8 intnum);
/* These are needed for DMA. */
extern void PutByteDMA(const uint32 Addr, const uint32 Value);
extern uint8 GetByteDMA(const uint32 Addr);
#define UNIT_V_DJHDC_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
#define UNIT_DJHDC_VERBOSE (1 << UNIT_V_DJHDC_VERBOSE)
#define DJHDC_CAPACITY (SCRIBE_NTRACKS * SCRIBE_NHEADS * \
SCRIBE_NSECTORS * SCRIBE_SECTSIZE) /* Default Disk Capacity */
static t_stat djhdc_reset(DEVICE *djhdc_dev);
static t_stat djhdc_attach(UNIT *uptr, CONST char *cptr);
static t_stat djhdc_detach(UNIT *uptr);
static t_stat djhdc_unit_set_geometry(UNIT* uptr, int32 value, CONST char* cptr, void* desc);
static t_stat djhdc_unit_show_geometry(FILE* st, UNIT* uptr, int32 value, CONST void* desc);
static int DJHDC_Validate_CHSN(DJHDC_DRIVE_INFO* pDrive);
#ifdef DJHDC_INTERRUPTS
static void raise_djhdc_interrupt(void);
#endif /* DJHDC_INTERRUPTS */
static const char* djhdc_description(DEVICE *dptr);
static int32 djhdcdev(const int32 port, const int32 io, const int32 data);
/* static uint8 DJHDC_Read(const uint32 Addr); */
static uint8 DJHDC_Write(const uint32 Addr, uint8 cData);
static UNIT djhdc_unit[] = {
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DJHDC_CAPACITY) },
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DJHDC_CAPACITY) },
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DJHDC_CAPACITY) },
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DJHDC_CAPACITY) }
};
static REG djhdc_reg[] = {
{ DRDATAD (NTRACKS, ntracks, 10,
"Number of tracks"), },
{ DRDATAD (NHEADS, nheads, 8,
"Number of heads"), },
{ DRDATAD (NSECTORS, nsectors, 8,
"Number of sectors per track"), },
{ DRDATAD (SECTSIZE, sectsize, 11,
"Sector size not including pre/postamble"), },
{ HRDATAD (SEL_DRIVE, djhdc_info_data.sel_drive, 3,
"Currently selected drive"), },
{ HRDATAD (MODE, djhdc_info_data.mode, 8,
"Mode (0xFF=absolute, 0x00=logical)"), },
{ HRDATAD (NDRIVES, djhdc_info_data.ndrives, 8,
"Number of drives attached to the controller"), },
{ HRDATAD (LINK_ADDR, djhdc_info_data.link_addr, 32,
"Link address for next IOPB"), },
{ HRDATAD (DMA_ADDR, djhdc_info_data.dma_addr, 32,
"DMA address for the current IOPB"), },
{ BRDATAD (IOPB, djhdc_info_data.iopb, 16, 8, 16,
"IOPB command register"), },
{ NULL }
};
#define DJHDC_NAME "Morrow HDC/DMA Hard Disk Controller"
static const char* djhdc_description(DEVICE *dptr) {
if (dptr == NULL) {
return NULL;
}
return DJHDC_NAME;
}
static MTAB djhdc_mod[] = {
{ MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE",
&set_iobase, &show_iobase, NULL, "Sets disk controller I/O base address" },
{ MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "GEOMETRY", "GEOMETRY",
&djhdc_unit_set_geometry, &djhdc_unit_show_geometry, NULL,
"Set disk geometry C:nnnn/H:n/S:nnn/N:nnnn" },
{ 0 }
};
/* Debug Flags */
static DEBTAB djhdc_dt[] = {
{ "ERROR", ERROR_MSG, "Error messages" },
{ "SEEK", SEEK_MSG, "Seek messages" },
{ "OPCODE", OPCODE_MSG, "Opcode messages" },
{ "READ", RD_DATA_MSG, "Read messages" },
{ "WRITE", WR_DATA_MSG, "Write messages" },
{ "IRQ", IRQ_MSG, "IRQ messages" },
{ "VERBOSE", VERBOSE_MSG, "Verbose messages" },
{ "FORMAT", FORMAT_MSG, "Format messages" },
{ NULL, 0 }
};
DEVICE djhdc_dev = {
DEV_NAME, djhdc_unit, djhdc_reg, djhdc_mod,
DJHDC_MAX_DRIVES, 10, 31, 1, DJHDC_MAX_DRIVES, DJHDC_MAX_DRIVES,
NULL, NULL, &djhdc_reset,
NULL, &djhdc_attach, &djhdc_detach,
&djhdc_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG,
djhdc_dt, NULL, NULL, NULL, NULL, NULL, &djhdc_description
};
/* Reset routine */
static t_stat djhdc_reset(DEVICE *dptr)
{
PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt;
if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */
sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &djhdcdev, "djhdcdev", TRUE);
} else {
/* Connect DJHDC at base address */
if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &djhdcdev, "djhdcdev", FALSE) != 0) {
sim_printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base);
return SCPE_ARG;
}
}
djhdc_info->link_addr = DJHDC_LINK_ADDR; /* After RESET, the link pointer is at 0x000050. */
return SCPE_OK;
}
/* Attach routine */
static t_stat djhdc_attach(UNIT *uptr, CONST char *cptr)
{
t_stat r = SCPE_OK;
DJHDC_DRIVE_INFO *pDrive;
int i = 0;
i = find_unit_index(uptr);
if (i == -1) {
return (SCPE_IERR);
}
pDrive = &djhdc_info->drive[i];
pDrive->ready = 1;
pDrive->track = 5;
if (pDrive->ntracks == 0) {
/* If geometry was not specified, default to Miniscribe 15MB */
pDrive->ntracks = SCRIBE_NTRACKS;
pDrive->nheads = SCRIBE_NHEADS;
pDrive->nsectors = SCRIBE_NSECTORS;
pDrive->sectsize = SCRIBE_SECTSIZE;
}
r = attach_unit(uptr, cptr); /* attach unit */
if ( r != SCPE_OK) /* error? */
return r;
/* Determine length of this disk */
if(sim_fsize(uptr->fileref) != 0) {
uptr->capac = sim_fsize(uptr->fileref);
} else {
uptr->capac = (pDrive->ntracks * pDrive->nsectors * pDrive->nheads * pDrive->sectsize);
}
pDrive->uptr = uptr;
/* Default for new file is DSK */
uptr->u3 = IMAGE_TYPE_DSK;
if(uptr->capac > 0) {
r = assignDiskType(uptr);
if (r != SCPE_OK) {
djhdc_detach(uptr);
return r;
}
}
if (uptr->flags & UNIT_DJHDC_VERBOSE)
sim_printf("DJHDC%d, attached to '%s', type=%s, len=%d\n", i, cptr,
uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK",
uptr->capac);
if(uptr->u3 == IMAGE_TYPE_IMD) {
if(uptr->capac < 318000) {
sim_printf("Cannot create IMD files with SIMH.\nCopy an existing file and format it with CP/M.\n");
djhdc_detach(uptr);
return SCPE_OPENERR;
}
if (uptr->flags & UNIT_DJHDC_VERBOSE)
sim_printf("--------------------------------------------------------\n");
djhdc_info->drive[i].imd = diskOpenEx((uptr->fileref), (uptr->flags & UNIT_DJHDC_VERBOSE),
&djhdc_dev, VERBOSE_MSG, VERBOSE_MSG);
if (uptr->flags & UNIT_DJHDC_VERBOSE)
sim_printf("\n");
} else {
djhdc_info->drive[i].imd = NULL;
}
return SCPE_OK;
}
/* Detach routine */
static t_stat djhdc_detach(UNIT *uptr)
{
DJHDC_DRIVE_INFO *pDrive;
t_stat r;
int32 i;
i = find_unit_index(uptr);
if (i == -1) {
return (SCPE_IERR);
}
pDrive = &djhdc_info->drive[i];
pDrive->ready = 0;
if (uptr->flags & UNIT_DJHDC_VERBOSE)
sim_printf("Detach DJHDC%d\n", i);
r = detach_unit(uptr); /* detach unit */
if ( r != SCPE_OK)
return r;
return SCPE_OK;
}
/* Set geometry of the disk drive */
static t_stat djhdc_unit_set_geometry(UNIT* uptr, int32 value, CONST char* cptr, void* desc)
{
DJHDC_DRIVE_INFO* pDrive;
int32 i;
int32 result;
uint16 newCyls, newHeads, newSPT, newSecLen;
i = find_unit_index(uptr);
if (i == -1) {
return (SCPE_IERR);
}
pDrive = &djhdc_info->drive[i];
if (cptr == NULL)
return SCPE_ARG;
result = sscanf(cptr, "C:%hd/H:%hd/S:%hd/N:%hd", &newCyls, &newHeads, &newSPT, &newSecLen);
if (result != 4)
return SCPE_ARG;
/* Validate Cyl, Heads, Sector, Length are valid for the HDC-1001 */
if (newCyls < 1 || newCyls > DJHDC_MAX_CYLS) {
sim_debug(ERROR_MSG, &djhdc_dev, DEV_NAME "%d: Number of cylinders must be 1-%d.\n",
djhdc_info->sel_drive, DJHDC_MAX_CYLS);
return SCPE_ARG;
}
if (newHeads < 1 || newHeads > DJHDC_MAX_HEADS) {
sim_debug(ERROR_MSG, &djhdc_dev, DEV_NAME "%d: Number of heads must be 1-%d.\n",
djhdc_info->sel_drive, DJHDC_MAX_HEADS);
return SCPE_ARG;
}
if (newSPT < 1 || newSPT > DJHDC_MAX_SPT) {
sim_debug(ERROR_MSG, &djhdc_dev, DEV_NAME "%d: Number of sectors per track must be 1-%d.\n",
djhdc_info->sel_drive, DJHDC_MAX_SPT);
return SCPE_ARG;
}
if (newSecLen != 2048 && newSecLen != 1024 && newSecLen != 512 && newSecLen != 256 && newSecLen != 128) {
sim_debug(ERROR_MSG, &djhdc_dev, DEV_NAME "%d: Sector length must be 128, 256, 512, 1024, or 2048.\n",
djhdc_info->sel_drive);
return SCPE_ARG;
}
pDrive->ntracks = newCyls;
pDrive->nheads = newHeads;
pDrive->nsectors = newSPT;
pDrive->sectsize = newSecLen;
return SCPE_OK;
}
/* Show geometry of the disk drive */
static t_stat djhdc_unit_show_geometry(FILE* st, UNIT* uptr, int32 value, CONST void* desc)
{
DJHDC_DRIVE_INFO* pDrive;
int32 i;
i = find_unit_index(uptr);
if (i == -1) {
return (SCPE_IERR);
}
pDrive = &djhdc_info->drive[i];
fprintf(st, "C:%d/H:%d/S:%d/N:%d",
pDrive->ntracks, pDrive->nheads, pDrive->nsectors, pDrive->sectsize);
return SCPE_OK;
}
static int32 djhdcdev(const int32 port, const int32 io, const int32 data)
{
sim_debug(VERBOSE_MSG, &djhdc_dev, DEV_NAME ": " ADDRESS_FORMAT
" IO %s, Port %02x\n", PCX, io ? "WR" : "RD", port);
if(io) {
DJHDC_Write(port, (uint8)data);
return 0;
} else {
return(0xFF);
}
}
static uint8 DJHDC_Write(const uint32 Addr, uint8 cData)
{
uint32 next_link;
uint8 result = DJHDC_STATUS_COMPLETE;
uint8 i;
uint8 opcode;
DJHDC_DRIVE_INFO *pDrive;
/* RESET */
if ((Addr & 1) == DJHDC_RESET) {
djhdc_info->link_addr = DJHDC_LINK_ADDR - DJHDC_IOPB_LINK;
sim_debug(VERBOSE_MSG, &djhdc_dev, DEV_NAME "[%d]: RESET\n",
djhdc_info->sel_drive);
return 0;
}
/* START */
/* Read first three bytes of IOPB (link address) */
for (i = DJHDC_IOPB_LINK; i < DJHDC_IOPB_LEN; i++) {
djhdc_info->iopb[i] = GetByteDMA((djhdc_info->link_addr) + i);
}
next_link = djhdc_info->iopb[DJHDC_IOPB_LINK + 0];
next_link |= djhdc_info->iopb[DJHDC_IOPB_LINK+1] << 8;
next_link |= djhdc_info->iopb[DJHDC_IOPB_LINK+2] << 16;
/* Point IOPB to new link */
djhdc_info->link_addr = next_link;
/* Read remainder of IOPB */
for(i = 0; i < DJHDC_IOPB_LEN-3; i++) {
djhdc_info->iopb[i] = GetByteDMA((djhdc_info->link_addr) + i);
}
/* Process the IOPB */
djhdc_info->iopb[DJHDC_IOPB_OPCODE] = djhdc_info->iopb[DJHDC_IOPB_OPCODE] & DJHDC_OPCODE_MASK;
opcode = djhdc_info->iopb[DJHDC_IOPB_OPCODE];
djhdc_info->sel_drive = djhdc_info->iopb[DJHDC_IOPB_SELDRV] & 0x03;
djhdc_info->step_dir = (djhdc_info->iopb[DJHDC_IOPB_SELDRV] & DJHDC_STEP_DIR) ? 1 : 0;
djhdc_info->steps = djhdc_info->iopb[DJHDC_IOPB_STEP_L];
djhdc_info->steps |= djhdc_info->iopb[DJHDC_IOPB_STEP_H] << 8;
djhdc_info->dma_addr = djhdc_info->iopb[DJHDC_IOPB_DMA_L];
djhdc_info->dma_addr |= djhdc_info->iopb[DJHDC_IOPB_DMA_H] << 8;
djhdc_info->dma_addr |= djhdc_info->iopb[DJHDC_IOPB_DMA_E] << 16;
sim_debug(VERBOSE_MSG, &djhdc_dev, DEV_NAME "[%d]: SEEK=%d %s, LINK=0x%05x, OPCODE=%x, %s DMA@0x%05x\n",
djhdc_info->sel_drive,
djhdc_info->steps,
djhdc_info->step_dir ? "OUT" : "IN",
djhdc_info->link_addr,
djhdc_info->iopb[DJHDC_IOPB_OPCODE],
djhdc_opcode_str[djhdc_info->iopb[DJHDC_IOPB_OPCODE]],
djhdc_info->dma_addr);
pDrive = &djhdc_info->drive[djhdc_info->sel_drive];
if(pDrive->ready) {
/* Seek phase */
if (djhdc_info->step_dir) {
/* Step Out */
if (djhdc_info->steps >= pDrive->cur_cyl) {
pDrive->cur_cyl = 0;
sim_debug(SEEK_MSG, &djhdc_dev, DEV_NAME "[%d]: HOME\n",
djhdc_info->sel_drive);
}
else {
pDrive->cur_cyl -= djhdc_info->steps;
}
}
else {
/* Step In */
pDrive->cur_cyl += djhdc_info->steps;
}
sim_debug(SEEK_MSG, &djhdc_dev, DEV_NAME "[%d]: Current track: %d\n",
djhdc_info->sel_drive,
pDrive->cur_cyl);
/* Perform command */
switch(opcode) {
case DJHDC_OPCODE_READ_DATA:
case DJHDC_OPCODE_WRITE_DATA:
{
uint32 track_len;
uint32 xfr_len;
uint32 file_offset;
uint32 xfr_count = 0;
uint8* dataBuffer;
size_t rtn;
pDrive->cur_cyl = djhdc_info->iopb[DJHDC_IOPB_ARG0] | (djhdc_info->iopb[DJHDC_IOPB_ARG1] << 8);
pDrive->cur_head = djhdc_info->iopb[DJHDC_IOPB_ARG2];
pDrive->cur_sect = djhdc_info->iopb[DJHDC_IOPB_ARG3] - 1;
if (DJHDC_Validate_CHSN(pDrive) != SCPE_OK) {
result = DJHDC_STATUS_HEADER_NOT_FOUND;
break;
}
track_len = pDrive->nsectors * pDrive->nheads * pDrive->sectsize;
file_offset = (pDrive->cur_cyl * track_len); /* Calculate offset based on current track */
file_offset += pDrive->nsectors * pDrive->cur_head * pDrive->sectsize;
file_offset += pDrive->cur_sect * pDrive->sectsize;
xfr_len = pDrive->sectsize;
dataBuffer = (uint8*)malloc(xfr_len);
if (dataBuffer == NULL) {
sim_printf("%s: error allocating memory\n", __FUNCTION__);
return (0);
}
if (sim_fseek((pDrive->uptr)->fileref, file_offset, SEEK_SET) == 0) {
if (opcode == DJHDC_OPCODE_READ_DATA) { /* Read */
rtn = sim_fread(dataBuffer, 1, xfr_len, (pDrive->uptr)->fileref);
sim_debug(RD_DATA_MSG, &djhdc_dev, DEV_NAME "[%d]: " ADDRESS_FORMAT
" READ @0x%05x C:%04d/H:%d/S:%04d len=%d, file_offset=%d, %s\n",
djhdc_info->sel_drive,
PCX,
djhdc_info->dma_addr,
pDrive->cur_cyl,
pDrive->cur_head,
pDrive->cur_sect,
xfr_len,
file_offset,
rtn == (size_t)xfr_len ? "OK" : "NOK");
/* Perform DMA Transfer */
for (xfr_count = 0; xfr_count < xfr_len; xfr_count++) {
PutByteDMA(djhdc_info->dma_addr + xfr_count, dataBuffer[xfr_count]);
}
}
else { /* Write */
sim_debug(WR_DATA_MSG, &djhdc_dev, DEV_NAME "[%d]: " ADDRESS_FORMAT
" WRITE @0x%05x C:%04d/H:%d/S:%04d file_offset=%d, len=%d\n",
djhdc_info->sel_drive,
PCX, djhdc_info->dma_addr,
pDrive->cur_cyl,
pDrive->cur_head,
pDrive->cur_sect,
file_offset,
xfr_len);
/* Perform DMA Transfer */
for (xfr_count = 0; xfr_count < xfr_len; xfr_count++) {
dataBuffer[xfr_count] = GetByteDMA(djhdc_info->dma_addr + xfr_count);
}
sim_fwrite(dataBuffer, 1, xfr_len, (pDrive->uptr)->fileref);
}
}
else {
sim_debug(ERROR_MSG, &djhdc_dev, DEV_NAME "[%d]: " ADDRESS_FORMAT " READWRITE: sim_fseek error.\n", djhdc_info->sel_drive, PCX);
}
free(dataBuffer);
break;
}
case DJHDC_OPCODE_READ_HEADER:
sim_debug(ERROR_MSG, &djhdc_dev, DEV_NAME "[%d]: " ADDRESS_FORMAT " READ_HEADER: not implemented.\n", djhdc_info->sel_drive, PCX);
result = DJHDC_STATUS_HEADER_NOT_FOUND;
break;
case DJHDC_OPCODE_FORMAT_TRACK:
{
uint32 track_len;
uint32 file_offset;
uint8* fmtBuffer;
uint8 head;
uint8 gap;
uint8 sector_count;
uint8 sector_size_code;
uint8 fill_byte;
head = ~(djhdc_info->iopb[DJHDC_IOPB_SEL_HD] >> 2) & 7;
gap = djhdc_info->iopb[DJHDC_IOPB_ARG0];
sector_count = 255 - djhdc_info->iopb[DJHDC_IOPB_ARG1];
sector_size_code = djhdc_info->iopb[DJHDC_IOPB_ARG2];
fill_byte = djhdc_info->iopb[DJHDC_IOPB_ARG3];
switch (sector_size_code) {
case 0xFF:
pDrive->cur_sectsize = 128;
break;
case 0xFE:
pDrive->cur_sectsize = 256;
break;
case 0xFC:
pDrive->cur_sectsize = 512;
break;
case 0xF8:
pDrive->cur_sectsize = 1024;
break;
case 0xF0:
pDrive->cur_sectsize = 2048;
break;
default:
sim_debug(ERROR_MSG, &djhdc_dev, DEV_NAME ": Invalid sector size code: 0x%02x.\n",
djhdc_info->sector_size_code);
pDrive->cur_sectsize = 0;
result = DJHDC_STATUS_ILLEGAL_COMMAND;
break;
}
if (DJHDC_Validate_CHSN(pDrive) != SCPE_OK) {
result = DJHDC_STATUS_HEADER_NOT_FOUND;
break;
}
track_len = pDrive->nheads * sector_count * pDrive->sectsize;
file_offset = pDrive->cur_cyl * track_len; /* Calculate offset based on current track */
file_offset += head * sector_count * pDrive->sectsize;
sim_debug(FORMAT_MSG, &djhdc_dev, DEV_NAME "[%d]: " ADDRESS_FORMAT
" FORMAT C:%d/H:%d, Gap=%d, Fill=0x%02x, Count=%d, Sector Size:=%d, file offset: 0x%08x\n",
djhdc_info->sel_drive,
PCX,
pDrive->cur_cyl,
head,
gap,
fill_byte,
sector_count,
pDrive->sectsize,
file_offset);
fmtBuffer = (uint8*)malloc(track_len);
if (fmtBuffer == NULL) {
sim_printf("%s: error allocating memory\n", __FUNCTION__);
return (0);
}
memset(fmtBuffer, fill_byte, track_len);
if (sim_fseek((pDrive->uptr)->fileref, file_offset, SEEK_SET) == 0) {
sim_fwrite(fmtBuffer, 1, track_len, (pDrive->uptr)->fileref);
}
else {
sim_debug(WR_DATA_MSG, &djhdc_dev, DEV_NAME "[%d]: " ADDRESS_FORMAT " FORMAT: sim_fseek error.\n", djhdc_info->sel_drive, PCX);
result = DJHDC_STATUS_WRITE_FAULT;
}
free(fmtBuffer);
break;
}
case DJHDC_OPCODE_LOAD_CONSTANTS:
djhdc_info->irq_enable = (djhdc_info->iopb[DJHDC_IOPB_ARG1] & DJHDC_IRQ_EN_MASK) ? 1 : 0;
djhdc_info->step_delay = djhdc_info->iopb[DJHDC_IOPB_ARG1] & ~DJHDC_IRQ_EN_MASK;
djhdc_info->head_settle_time = djhdc_info->iopb[DJHDC_IOPB_ARG2];
djhdc_info->sector_size_code = djhdc_info->iopb[DJHDC_IOPB_ARG3];
switch (djhdc_info->sector_size_code) {
case 0x00:
pDrive->cur_sectsize = 128;
break;
case 0x01:
pDrive->cur_sectsize = 256;
break;
case 0x03:
pDrive->cur_sectsize = 512;
break;
case 0x07:
pDrive->cur_sectsize = 1024;
break;
case 0x0F:
pDrive->cur_sectsize = 2048;
break;
default:
sim_debug(ERROR_MSG, &djhdc_dev, DEV_NAME ": Invalid sector size code: 0x%02x.\n",
djhdc_info->sector_size_code);
pDrive->cur_sectsize = 0;
result = DJHDC_STATUS_ILLEGAL_COMMAND;
break;
}
sim_debug(VERBOSE_MSG, &djhdc_dev, DEV_NAME "[%d]: " ADDRESS_FORMAT
" Load Constants: Interrupt Enable: %d, step delay: %d, head settle time: %d, sector size %d (code: 0x%02x)\n",
djhdc_info->sel_drive, PCX,
djhdc_info->irq_enable,
djhdc_info->step_delay,
djhdc_info->head_settle_time,
pDrive->sectsize,
djhdc_info->sector_size_code);
break;
case DJHDC_OPCODE_SENSE_STATUS:
sim_debug(ERROR_MSG, &djhdc_dev, DEV_NAME "[%d]: " ADDRESS_FORMAT " SENSE_STATUS: not implemented.\n", djhdc_info->sel_drive, PCX);
result = DJHDC_DRIVE_READY_SIGNAL;
if (pDrive->cur_cyl != 0) result = DJHDC_TRACK_0_DETECT;
break;
case DJHDC_OPCODE_NOOP:
sim_debug(VERBOSE_MSG, &djhdc_dev, DEV_NAME "[%d]: " ADDRESS_FORMAT
" NOOP\n", djhdc_info->sel_drive, PCX);
break;
default:
sim_debug(ERROR_MSG, &djhdc_dev, DEV_NAME "[%d]: " ADDRESS_FORMAT
" OPCODE=%x Unsupported\n",
djhdc_info->sel_drive,
PCX,
opcode & DJHDC_OPCODE_MASK);
result = DJHDC_STATUS_ILLEGAL_COMMAND;
break;
}
} else { /* Drive not ready */
result = DJHDC_STATUS_NOT_READY;
}
/* Return status */
djhdc_info->iopb[DJHDC_IOPB_STATUS] = result;
/* Update IOPB in host memory */
PutByteDMA(djhdc_info->link_addr + DJHDC_IOPB_STATUS, djhdc_info->iopb[DJHDC_IOPB_STATUS]);
#ifdef DJHDC_INTERRUPTS
if(djhdc_info->irq_enable) {
raise_djhdc_interrupt();
}
#endif /* DJHDC_INTERRUPTS */
return 0;
}
/* Validate that Cyl, Head, Sector, Sector Length are valid for the current
* disk drive geometry.
*/
static int DJHDC_Validate_CHSN(DJHDC_DRIVE_INFO* pDrive)
{
int status = SCPE_OK;
/* Check to make sure we're operating on a valid C/H/S/N. */
if ((pDrive->cur_cyl >= pDrive->ntracks) ||
(pDrive->cur_head >= pDrive->nheads) ||
(pDrive->cur_sect >= pDrive->nsectors) ||
(pDrive->cur_sectsize != pDrive->sectsize))
{
sim_debug(ERROR_MSG, &djhdc_dev, DEV_NAME "%d: " ADDRESS_FORMAT
" ID Not Found (check disk geometry.)\n", djhdc_info->sel_drive, PCX);
status = SCPE_IOERR;
}
return (status);
}
#ifdef DJHDC_INTERRUPTS
static void raise_djhdc_interrupt(void)
{
sim_debug(IRQ_MSG, &djhdc_dev, DEV_NAME ": " ADDRESS_FORMAT " Interrupt\n", PCX);
raise_scp300f_interrupt(DJHDC_INT);
}
#endif /* DJHDC_INTERRUPTS */