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

614 lines
23 KiB
C

/*************************************************************************
* *
* Copyright (c) 2021-2023 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. *
* *
* Module Description: *
* IBC/Integrated Business Computers SMD Hard Disk Controller module *
* for SIMH. *
* *
*************************************************************************/
#include "altairz80_defs.h"
#include "sim_imd.h"
/* Debug flags */
#define ERROR_MSG (1 << 0)
#define CMD_MSG (1 << 1)
#define RD_DATA_MSG (1 << 2)
#define WR_DATA_MSG (1 << 3)
#define FIFO_MSG (1 << 4)
#define REG_MSG (1 << 5)
#define VERBOSE_MSG (1 << 6)
#define IBC_SMD_MAX_DRIVES 2 /* Maximum number of drives supported */
#define IBC_SMD_MAX_SECLEN 1024 /* Maximum of 1024 bytes per sector */
#define IBC_SMD_MAX_CYLS 1024
#define IBC_SMD_MAX_HEADS 8
#define IBC_SMD_MAX_SPT 256
#define DEV_NAME "IBCSMD"
#define IBC_SMD_STATUS_ERROR (1 << 0)
#define IBC_SMD_ERROR_ID_NOT_FOUND (1 << 4)
#define IBC_SMD_CMD_00 0x00
#define IBC_SMD_CMD_SELECT_UNIT 0x10
#define IBC_SMD_CMD_SET_CYL 0x20
#define IBC_SMD_CMD_SET_HEAD 0x40
#define IBC_SMD_CMD_REZERO 0x80
#define IBC_SMD_CMD_WRITE_SECT 0x81
#define IBC_SMD_CMD_READ_SECT 0x88
#define IBC_SMD_REG_ERROR 0x0 /* Read */
#define IBC_SMD_REG_ARG0 0x0 /* Write */
#define IBC_SMD_REG_ARG1 0x1 /* Write */
#define IBC_SMD_REG_CMD 0x2 /* Write */
#define IBC_SMD_REG_SEC 0x3 /* Write */
#define IBC_SMD_REG_STATUS 0x7
#define IBC_SMD_REG_DATA 0x4
#define IBC_SMD_REG_SECID 0x7
typedef struct {
UNIT *uptr;
uint8 readonly; /* Drive is read-only? */
uint16 sectsize; /* sector size */
uint16 nsectors; /* number of sectors/track */
uint16 nheads; /* number of heads */
uint16 ncyls; /* number of cylinders */
uint16 cur_cyl; /* Current cylinder */
uint8 cur_head; /* Current Head */
uint8 cur_sect; /* current starting sector of transfer */
uint8 ready; /* Is drive ready? */
} IBC_SMD_DRIVE_INFO;
typedef struct {
PNP_INFO pnp; /* Plug and Play */
uint8 intenable; /* Interrupt Enable */
uint8 intvector; /* Interrupt Vector */
uint8 sel_drive; /* Currently selected drive */
uint8 arg0; /* IBC SMD Argument 0 Register */
uint8 arg1; /* IBC SMD Argument 1 Register */
uint8 cmd; /* IBC SMD Command Register */
uint8 sec; /* IBC SMD Sector Register */
uint8 status_reg; /* Status Register */
uint8 error_reg; /* Error Register */
uint8 retries; /* Number of retries to attempt */
uint8 ndrives; /* Number of drives attached to the controller */
uint8 sectbuf[IBC_SMD_MAX_SECLEN];
uint16 secbuf_index;
IBC_SMD_DRIVE_INFO drive[IBC_SMD_MAX_DRIVES];
} IBC_SMD_INFO;
static IBC_SMD_INFO ibc_smd_info_data = { { 0x0, 0, 0x40, 8 } };
static IBC_SMD_INFO *ibc_smd_info = &ibc_smd_info_data;
extern uint32 PCX;
extern int32 vectorInterrupt; /* Interrupt pending */
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);
#define UNIT_V_IBC_SMD_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
#define UNIT_IBC_SMD_VERBOSE (1 << UNIT_V_IBC_SMD_VERBOSE)
#define IBC_SMD_CAPACITY (512*4*16*512) /* Default Disk Capacity Quantum 2020 */
static t_stat ibc_smd_reset(DEVICE *ibc_smd_dev);
static t_stat ibc_smd_attach(UNIT *uptr, CONST char *cptr);
static t_stat ibc_smd_detach(UNIT *uptr);
static t_stat ibc_smd_unit_set_geometry(UNIT* uptr, int32 value, CONST char* cptr, void* desc);
static t_stat ibc_smd_unit_show_geometry(FILE* st, UNIT* uptr, int32 value, CONST void* desc);
static int32 ibcsmddev(const int32 port, const int32 io, const int32 data);
static uint8 IBC_SMD_Read(const uint32 Addr);
static uint8 IBC_SMD_Write(const uint32 Addr, uint8 cData);
static t_stat IBC_SMD_doCommand(void);
static const char* ibc_smd_description(DEVICE *dptr);
static UNIT ibc_smd_unit[] = {
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, IBC_SMD_CAPACITY) },
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, IBC_SMD_CAPACITY) },
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, IBC_SMD_CAPACITY) },
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, IBC_SMD_CAPACITY) }
};
static REG ibc_smd_reg[] = {
{ HRDATAD (SMD_ERROR, ibc_smd_info_data.error_reg, 8, "SMD Error Register"), },
{ HRDATAD (SMD_STATUS, ibc_smd_info_data.status_reg, 8, "SMD Status Register"), },
{ HRDATAD (SMD_ARG0, ibc_smd_info_data.arg0, 8, "SMD ARG0 Register"), },
{ HRDATAD (SMD_ARG1, ibc_smd_info_data.arg1, 8, "SMD ARG1 Register"), },
{ HRDATAD (SMD_CMD, ibc_smd_info_data.cmd, 8, "SMD Command Register"), },
{ HRDATAD (SMD_SEC, ibc_smd_info_data.sec, 8, "SMD Sector Register"), },
{ FLDATAD (INTENABLE, ibc_smd_info_data.intenable, 1, "SMD Interrupt Enable"), },
{ DRDATAD (INTVECTOR, ibc_smd_info_data.intvector, 8, "SMD Interrupt Vector"), },
{ NULL }
};
#define IBC_SMD_NAME "IBC SMD Hard Disk Controller"
static const char* ibc_smd_description(DEVICE *dptr) {
if (dptr == NULL) {
return NULL;
}
return IBC_SMD_NAME;
}
static MTAB ibc_smd_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",
&ibc_smd_unit_set_geometry, &ibc_smd_unit_show_geometry, NULL,
"Set disk geometry C:nnnn/H:n/S:nnn/N:nnnn" },
{ 0 }
};
/* Debug Flags */
static DEBTAB ibc_smd_dt[] = {
{ "ERROR", ERROR_MSG, "Error messages" },
{ "REG", REG_MSG, "Register messages" },
{ "CMD", CMD_MSG, "Command messages" },
{ "READ", RD_DATA_MSG, "Read messages" },
{ "WRITE", WR_DATA_MSG, "Write messages" },
{ "FIFO", FIFO_MSG, "FIFO messages" },
{ "VERBOSE", VERBOSE_MSG, "Verbose messages" },
{ NULL, 0 }
};
DEVICE ibc_smd_dev = {
DEV_NAME, ibc_smd_unit, ibc_smd_reg, ibc_smd_mod,
IBC_SMD_MAX_DRIVES, 10, 31, 1, IBC_SMD_MAX_DRIVES, IBC_SMD_MAX_DRIVES,
NULL, NULL, &ibc_smd_reset,
NULL, &ibc_smd_attach, &ibc_smd_detach,
&ibc_smd_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG,
ibc_smd_dt, NULL, NULL, NULL, NULL, NULL, &ibc_smd_description
};
/* Reset routine */
static t_stat ibc_smd_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, &ibcsmddev, "ibcsmddev", TRUE);
} else {
/* Connect IBC_SMD at base address */
if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &ibcsmddev, "ibcsmddev", FALSE) != 0) {
sim_printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base);
return SCPE_ARG;
}
}
ibc_smd_info->status_reg = 0xd1;
ibc_smd_info->error_reg = 0x80;
ibc_smd_info->sel_drive = 0;
return SCPE_OK;
}
/* Attach routine */
static t_stat ibc_smd_attach(UNIT *uptr, CONST char *cptr)
{
t_stat r = SCPE_OK;
IBC_SMD_DRIVE_INFO *pDrive;
int i = 0;
i = find_unit_index(uptr);
if (i == -1) {
return (SCPE_IERR);
}
pDrive = &ibc_smd_info->drive[i];
/* Defaults for the Quantum 2020 Drive */
pDrive->ready = 0;
if (pDrive->ncyls == 0) {
/* If geometry was not specified, default to Quantum 2020 */
pDrive->ncyls = 512;
pDrive->nheads = 4;
pDrive->nsectors = 16;
pDrive->sectsize = 512;
}
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->ncyls * 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) {
ibc_smd_detach(uptr);
return r;
}
}
sim_debug(VERBOSE_MSG, &ibc_smd_dev, DEV_NAME "%d, attached to '%s', type=DSK, len=%d\n",
i, cptr, uptr->capac);
pDrive->readonly = (uptr->flags & UNIT_RO) ? 1 : 0;
ibc_smd_info->error_reg = 0;
pDrive->ready = 1;
ibc_smd_info->status_reg = 0;
return SCPE_OK;
}
/* Detach routine */
static t_stat ibc_smd_detach(UNIT *uptr)
{
IBC_SMD_DRIVE_INFO *pDrive;
t_stat r;
int32 i;
i = find_unit_index(uptr);
if (i == -1) {
return (SCPE_IERR);
}
pDrive = &ibc_smd_info->drive[i];
pDrive->ready = 0;
sim_debug(VERBOSE_MSG, &ibc_smd_dev, "Detach " DEV_NAME "%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 ibc_smd_unit_set_geometry(UNIT* uptr, int32 value, CONST char* cptr, void* desc)
{
IBC_SMD_DRIVE_INFO* pDrive;
int32 i;
int32 result;
uint16 newCyls, newHeads, newSPT, newSecLen;
i = find_unit_index(uptr);
if (i == -1) {
return (SCPE_IERR);
}
pDrive = &ibc_smd_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 */
if (newCyls < 1 || newCyls > IBC_SMD_MAX_CYLS) {
sim_debug(ERROR_MSG, &ibc_smd_dev, DEV_NAME "%d: Number of cylinders must be 1-%d.\n",
ibc_smd_info->sel_drive, IBC_SMD_MAX_CYLS);
return SCPE_ARG;
}
if (newHeads < 1 || newHeads > IBC_SMD_MAX_HEADS) {
sim_debug(ERROR_MSG, &ibc_smd_dev, DEV_NAME "%d: Number of heads must be 1-%d.\n",
ibc_smd_info->sel_drive, IBC_SMD_MAX_HEADS);
return SCPE_ARG;
}
if (newSPT < 1 || newSPT > IBC_SMD_MAX_SPT) {
sim_debug(ERROR_MSG, &ibc_smd_dev, DEV_NAME "%d: Number of sectors per track must be 1-%d.\n",
ibc_smd_info->sel_drive, IBC_SMD_MAX_SPT);
return SCPE_ARG;
}
if (newSecLen != 512 && newSecLen != 256 && newSecLen != 128) {
sim_debug(ERROR_MSG, &ibc_smd_dev,DEV_NAME "%d: Sector length must be 128, 256, or 512.\n",
ibc_smd_info->sel_drive);
return SCPE_ARG;
}
pDrive->ncyls = newCyls;
pDrive->nheads = newHeads;
pDrive->nsectors = newSPT;
pDrive->sectsize = newSecLen;
return SCPE_OK;
}
/* Show geometry of the disk drive */
static t_stat ibc_smd_unit_show_geometry(FILE* st, UNIT* uptr, int32 value, CONST void* desc)
{
IBC_SMD_DRIVE_INFO* pDrive;
int32 i;
i = find_unit_index(uptr);
if (i == -1) {
return (SCPE_IERR);
}
pDrive = &ibc_smd_info->drive[i];
fprintf(st, "C:%d/H:%d/S:%d/N:%d",
pDrive->ncyls, pDrive->nheads, pDrive->nsectors, pDrive->sectsize);
return SCPE_OK;
}
/* IBC SMD I/O Dispatch */
static int32 ibcsmddev(const int32 port, const int32 io, const int32 data)
{
if(io) {
IBC_SMD_Write(port, (uint8)data);
return 0;
} else {
return(IBC_SMD_Read(port));
}
}
/* I/O Write to IBC SMD Registers */
static uint8 IBC_SMD_Write(const uint32 Addr, uint8 cData)
{
switch(Addr & 7) {
case IBC_SMD_REG_ARG0:
switch (cData) {
case 0x00:
ibc_smd_info->error_reg = 0x0;
break;
case 0x01:
ibc_smd_info->error_reg = 0x20;
break;
case IBC_SMD_CMD_READ_SECT:
ibc_smd_info->error_reg = 0x30;
break;
default:
ibc_smd_info->error_reg = 0x30;
break;
}
ibc_smd_info->arg0 = cData;
sim_debug(REG_MSG, &ibc_smd_dev, DEV_NAME ": " ADDRESS_FORMAT
" WR SMD_ARG0 0x%02x=0x%02x\n", PCX, Addr, cData);
break;
case IBC_SMD_REG_ARG1:
ibc_smd_info->arg1 = cData;
sim_debug(REG_MSG, &ibc_smd_dev, DEV_NAME ": " ADDRESS_FORMAT
" WR SMD_ARG1 0x%02x=0x%02x\n", PCX, Addr, cData);
break;
case IBC_SMD_REG_CMD:
ibc_smd_info->cmd = cData;
sim_debug(REG_MSG, &ibc_smd_dev, DEV_NAME ": " ADDRESS_FORMAT
" WR SMD_CMD 0x%02x=0x%02x\n", PCX, Addr, cData);
IBC_SMD_doCommand();
break;
case IBC_SMD_REG_SEC:
ibc_smd_info->sec = cData;
sim_debug(REG_MSG, &ibc_smd_dev, DEV_NAME ": " ADDRESS_FORMAT
" WR SMD_SEC 0x%02x=0x%02x\n", PCX, Addr, cData);
break;
case IBC_SMD_REG_SECID:
sim_debug(REG_MSG, &ibc_smd_dev, DEV_NAME ": " ADDRESS_FORMAT
" WR SECID 0x%02x=0x%02x\n", PCX, Addr, cData);
ibc_smd_info->secbuf_index = 0;
break;
case IBC_SMD_REG_DATA:
sim_debug(FIFO_MSG, &ibc_smd_dev, DEV_NAME ": " ADDRESS_FORMAT
" WR FIFO 0x%02x=0x%02x\n", PCX, Addr, cData);
ibc_smd_info->sectbuf[ibc_smd_info->secbuf_index] = cData;
ibc_smd_info->secbuf_index++;
break;
default:
sim_debug(ERROR_MSG, &ibc_smd_dev, DEV_NAME ": " ADDRESS_FORMAT
" Unhandled WR 0x%02x=0x%02x\n", PCX, Addr, cData);
break;
}
return 0;
}
/* I/O Read from IBC SMD Registers */
static uint8 IBC_SMD_Read(const uint32 Addr)
{
uint8 cData = 0xFF;
switch (Addr & 7) {
case IBC_SMD_REG_ERROR:
cData = ibc_smd_info->error_reg;
sim_debug(REG_MSG, &ibc_smd_dev, DEV_NAME ": " ADDRESS_FORMAT
" RD ERROR 0x%02x=0x%02x\n", PCX, Addr, cData);
break;
case 0x1:
cData = 0x7f;
sim_debug(ERROR_MSG, &ibc_smd_dev, DEV_NAME ": " ADDRESS_FORMAT
" RD Unknown 0x%02x=0x%02x\n", PCX, Addr, cData);
case IBC_SMD_REG_DATA:
cData = ibc_smd_info->sectbuf[ibc_smd_info->secbuf_index];
sim_debug(FIFO_MSG, &ibc_smd_dev, DEV_NAME ": " ADDRESS_FORMAT
" RD DATA[0x%02x]=0x%02x\n", PCX, ibc_smd_info->secbuf_index, cData);
ibc_smd_info->secbuf_index++;
break;
case IBC_SMD_REG_STATUS:
cData = ibc_smd_info->status_reg;
sim_debug(REG_MSG, &ibc_smd_dev,DEV_NAME ": " ADDRESS_FORMAT
" RD STATUS 0x%02x=0x%02x\n", PCX, Addr, cData);
break;
default:
sim_debug(ERROR_MSG, &ibc_smd_dev, DEV_NAME ": " ADDRESS_FORMAT
" Unhandled RD 0x%02x=0x%02x\n", PCX, Addr, cData);
break;
}
return (cData);
}
/* Validate that Cyl, Head, Sector are valid for the current
* disk drive geometry.
*/
static int IBC_SMD_Validate_CHSN(IBC_SMD_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->ncyls) ||
(pDrive->cur_head >= pDrive->nheads) ||
(pDrive->cur_sect >= pDrive->nsectors))
{
/* Set error bit in status register. */
ibc_smd_info->status_reg |= IBC_SMD_STATUS_ERROR;
/* Set ID_NOT_FOUND bit in error register. */
ibc_smd_info->error_reg |= IBC_SMD_ERROR_ID_NOT_FOUND;
sim_debug(ERROR_MSG, &ibc_smd_dev,DEV_NAME "%d: " ADDRESS_FORMAT
" C:%d/H:%d/S:%d: ID Not Found (check disk geometry.)\n", ibc_smd_info->sel_drive, PCX,
pDrive->cur_cyl,
pDrive->cur_head,
pDrive->cur_sect);
status = SCPE_IOERR;
}
else {
/* Clear ID_NOT_FOUND bit in error register. */
ibc_smd_info->error_reg &= ~IBC_SMD_ERROR_ID_NOT_FOUND;
}
return (status);
}
/* Perform SMD Disk Command */
static t_stat IBC_SMD_doCommand(void)
{
t_stat r = SCPE_OK;
IBC_SMD_DRIVE_INFO* pDrive = &ibc_smd_info->drive[ibc_smd_info->sel_drive];
uint8 cmd = ibc_smd_info->cmd;
switch (cmd) {
case IBC_SMD_CMD_00:
break;
case IBC_SMD_CMD_SELECT_UNIT:
ibc_smd_info->sel_drive = (ibc_smd_info->arg0 >> 4) & 1;
sim_debug(CMD_MSG, &ibc_smd_dev, DEV_NAME "%d: " ADDRESS_FORMAT
" 0x%02x: Select Unit %d\n",
ibc_smd_info->sel_drive, PCX,
cmd, ibc_smd_info->arg0 >> 4);
break;
case IBC_SMD_CMD_SET_CYL: /* Set Cyl */
pDrive->cur_cyl = ibc_smd_info->arg0 << 8;
pDrive->cur_cyl |= ibc_smd_info->arg1;
sim_debug(CMD_MSG, &ibc_smd_dev, DEV_NAME "%d: " ADDRESS_FORMAT
" 0x%02x: Set Cylinder %d\n",
ibc_smd_info->sel_drive, PCX,
cmd, pDrive->cur_cyl);
break;
case IBC_SMD_CMD_SET_HEAD: /* Set Head */
pDrive->cur_head = ibc_smd_info->arg1;
sim_debug(CMD_MSG, &ibc_smd_dev, DEV_NAME "%d: " ADDRESS_FORMAT
" 0x%02x: Set Head %d\n",
ibc_smd_info->sel_drive, PCX,
cmd, pDrive->cur_head);
break;
case IBC_SMD_CMD_REZERO:
sim_debug(CMD_MSG, &ibc_smd_dev, DEV_NAME "%d: " ADDRESS_FORMAT
" 0x%02x: Rezero %d\n",
ibc_smd_info->sel_drive, PCX,
cmd, ibc_smd_info->arg1);
ibc_smd_info->status_reg = 0xd1;
break;
case IBC_SMD_CMD_READ_SECT:
case IBC_SMD_CMD_WRITE_SECT:
{
uint32 xfr_len;
uint32 file_offset;
pDrive->cur_sect = ibc_smd_info->sec;
ibc_smd_info->secbuf_index = 0;
ibc_smd_info->sectbuf[0] = (pDrive->cur_cyl >> 8) & 0xFF;
ibc_smd_info->sectbuf[1] = pDrive->cur_cyl & 0xFF;
ibc_smd_info->sectbuf[2] = pDrive->cur_head;
ibc_smd_info->sectbuf[3] = pDrive->cur_sect;
/* Abort the read/write operation if C/H/S/N is not valid. */
if (IBC_SMD_Validate_CHSN(pDrive) != SCPE_OK) break;
/* Calculate file offset */
file_offset = (pDrive->cur_cyl * pDrive->nheads * pDrive->nsectors); /* Full cylinders */
file_offset += (pDrive->cur_head * pDrive->nsectors); /* Add full heads */
file_offset += (pDrive->cur_sect); /* Add sectors for current request */
file_offset *= pDrive->sectsize; /* Convert #sectors to byte offset */
xfr_len = pDrive->sectsize;
if (0 != (r = sim_fseek((pDrive->uptr)->fileref, file_offset, SEEK_SET)))
break;
if (cmd == IBC_SMD_CMD_READ_SECT) { /* Read */
sim_debug(RD_DATA_MSG, &ibc_smd_dev, DEV_NAME "%d: " ADDRESS_FORMAT
" RD SECTOR C:%04d/H:%d/S:%04d, offset=%5x, len=%d\n",
ibc_smd_info->sel_drive, PCX,
pDrive->cur_cyl, pDrive->cur_head, pDrive->cur_sect,
file_offset, xfr_len);
if (sim_fread(&ibc_smd_info->sectbuf[4], 1, xfr_len, (pDrive->uptr)->fileref) != xfr_len) {
r = SCPE_IOERR;
}
}
else { /* Write */
sim_debug(WR_DATA_MSG, &ibc_smd_dev, DEV_NAME "%d: " ADDRESS_FORMAT
" WR SECTOR C:%04d/H:%d/S:%04d offset=%5x, len=%d\n",
ibc_smd_info->sel_drive, PCX,
pDrive->cur_cyl, pDrive->cur_head, pDrive->cur_sect,
file_offset, xfr_len);
if (sim_fwrite(&ibc_smd_info->sectbuf[4], 1, xfr_len, (pDrive->uptr)->fileref) != xfr_len) {
r = SCPE_IOERR;
}
}
break;
}
default:
sim_debug(ERROR_MSG, &ibc_smd_dev, DEV_NAME "%d: " ADDRESS_FORMAT
" UNKNOWN COMMAND 0x%02x\n",
ibc_smd_info->sel_drive, PCX,
cmd);
break;
}
return r;
}