simh-testsetgenerator/AltairZ80/s100_djhdc.c
Mark Pizzolato decbe5b76b Various simulators: Set line endings to CRLF for consistency, remove stray tabs
Project standard source code has tabs converted to spaces and CRLF line
endings.

Other text files have CRLF line endings.
2023-03-19 16:51:27 -04: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 remaineder 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 */