AltairZ80: Add IBC Super Cadet / Middi Cadet support.
This commit is contained in:
parent
f0b782b33a
commit
15d2b89573
6 changed files with 3605 additions and 3 deletions
|
@ -60,6 +60,11 @@ extern DEVICE selchan_dev;
|
|||
extern DEVICE ss1_dev;
|
||||
extern DEVICE if3_dev;
|
||||
extern DEVICE i8272_dev;
|
||||
extern DEVICE ibc_dev;
|
||||
extern DEVICE ibc_hdc_dev;
|
||||
extern DEVICE ibc_smd_dev;
|
||||
extern DEVICE ibctimer_device;
|
||||
extern DEVICE ibcrtctimer_device;
|
||||
extern DEVICE mdriveh_dev;
|
||||
extern DEVICE switchcpu_dev;
|
||||
|
||||
|
@ -127,6 +132,12 @@ DEVICE *sim_devices[] = {
|
|||
&disk1a_dev, &disk2_dev, &disk3_dev, &ss1_dev, &mdriveh_dev, &selchan_dev, &if3_dev,
|
||||
/* Cromemco Devices */
|
||||
&cromfdc_dev,
|
||||
/* Integrated Business Computers (IBC) Devices */
|
||||
&ibc_dev,
|
||||
&ibctimer_device,
|
||||
&ibcrtctimer_device,
|
||||
&ibc_hdc_dev,
|
||||
&ibc_smd_dev,
|
||||
/* IMSAI Devices */
|
||||
&fif_dev,
|
||||
/* Micropolis Devices */
|
||||
|
|
2236
AltairZ80/ibc.c
Normal file
2236
AltairZ80/ibc.c
Normal file
File diff suppressed because it is too large
Load diff
679
AltairZ80/ibc_mcc_hdc.c
Normal file
679
AltairZ80/ibc_mcc_hdc.c
Normal file
|
@ -0,0 +1,679 @@
|
|||
/*************************************************************************
|
||||
* *
|
||||
* 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 MCC ST-506 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 TF_MSG (1 << 5)
|
||||
#define VERBOSE_MSG (1 << 6)
|
||||
|
||||
#define IBC_HDC_MAX_DRIVES 4 /* Maximum number of drives supported */
|
||||
#define IBC_HDC_MAX_SECLEN 256 /* Maximum of 256 bytes per sector */
|
||||
#define IBC_HDC_FORMAT_FILL_BYTE 0xe5 /* Real controller uses 0, but we
|
||||
choose 0xe5 so the disk shows up as blank under CP/M. */
|
||||
#define IBC_HDC_MAX_CYLS 1024
|
||||
#define IBC_HDC_MAX_HEADS 16
|
||||
#define IBC_HDC_MAX_SPT 256
|
||||
|
||||
#define DEV_NAME "IBCHDC"
|
||||
|
||||
/* Task File Register Offsets */
|
||||
#define TF_CSEC 0
|
||||
#define TF_HEAD 1
|
||||
#define TF_NSEC 2
|
||||
#define TF_SA3 3
|
||||
#define TF_CMD 4
|
||||
#define TF_DRIVE 5
|
||||
#define TF_TRKL 6
|
||||
#define TF_TRKH 7
|
||||
#define TF_FIFO 8
|
||||
|
||||
#define IBC_HDC_STATUS_ERROR (1 << 0)
|
||||
|
||||
#define IBC_HDC_ERROR_ID_NOT_FOUND (1 << 4)
|
||||
|
||||
#define IBC_HDC_CMD_RESET 0x00
|
||||
#define IBC_HDC_CMD_READ_SECT 0x01
|
||||
#define IBC_HDC_CMD_WRITE_SECT 0x02
|
||||
#define IBC_HDC_CMD_FORMAT_TRK 0x08
|
||||
#define IBC_HDC_CMD_ACCESS_FIFO 0x0b
|
||||
#define IBC_HDC_CMD_READ_PARAMETERS 0x10
|
||||
|
||||
#define IBC_HDC_REG_STATUS 0x40
|
||||
#define IBC_HDC_REG_FIFO_STATUS 0x44
|
||||
#define IBC_HDC_REG_FIFO 0x48
|
||||
|
||||
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 */
|
||||
uint16 cur_sectsize;/* Current sector size in SA6 register */
|
||||
uint16 xfr_nsects; /* Number of sectors to transfer */
|
||||
uint8 ready; /* Is drive ready? */
|
||||
} IBC_HDC_DRIVE_INFO;
|
||||
|
||||
typedef struct {
|
||||
PNP_INFO pnp; /* Plug and Play */
|
||||
uint8 sel_drive; /* Currently selected drive */
|
||||
uint8 reg_temp_holding[4];
|
||||
uint8 taskfile[9]; /* ATA Task File Registers */
|
||||
uint8 status_reg; /* IBC Disk Slave Status Register */
|
||||
uint8 error_reg; /* IBC Disk Slave Error Register */
|
||||
uint8 ndrives; /* Number of drives attached to the controller */
|
||||
uint8 sectbuf[IBC_HDC_MAX_SECLEN*10];
|
||||
uint16 secbuf_index;
|
||||
IBC_HDC_DRIVE_INFO drive[IBC_HDC_MAX_DRIVES];
|
||||
} IBC_HDC_INFO;
|
||||
|
||||
static IBC_HDC_INFO ibc_hdc_info_data = { { 0x0, 0, 0x40, 9 } };
|
||||
static IBC_HDC_INFO *ibc_hdc_info = &ibc_hdc_info_data;
|
||||
|
||||
extern uint32 PCX;
|
||||
extern int32 HL_S; /* HL register */
|
||||
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_HDC_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
|
||||
#define UNIT_IBC_HDC_VERBOSE (1 << UNIT_V_IBC_HDC_VERBOSE)
|
||||
#define IBC_HDC_CAPACITY (512*4*32*256) /* Default Disk Capacity Quantum 2020 */
|
||||
|
||||
static t_stat ibc_hdc_reset(DEVICE *ibc_hdc_dev);
|
||||
static t_stat ibc_hdc_attach(UNIT *uptr, CONST char *cptr);
|
||||
static t_stat ibc_hdc_detach(UNIT *uptr);
|
||||
static t_stat ibc_hdc_unit_set_geometry(UNIT* uptr, int32 value, CONST char* cptr, void* desc);
|
||||
static t_stat ibc_hdc_unit_show_geometry(FILE* st, UNIT* uptr, int32 value, CONST void* desc);
|
||||
static int32 ibchdcdev(const int32 port, const int32 io, const int32 data);
|
||||
|
||||
static uint8 IBC_HDC_Read(const uint32 Addr);
|
||||
static uint8 IBC_HDC_Write(const uint32 Addr, uint8 cData);
|
||||
static t_stat IBC_HDC_doCommand(void);
|
||||
static const char* ibc_hdc_description(DEVICE *dptr);
|
||||
|
||||
static UNIT ibc_hdc_unit[] = {
|
||||
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, IBC_HDC_CAPACITY) },
|
||||
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, IBC_HDC_CAPACITY) },
|
||||
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, IBC_HDC_CAPACITY) },
|
||||
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, IBC_HDC_CAPACITY) }
|
||||
};
|
||||
|
||||
static REG ibc_hdc_reg[] = {
|
||||
{ HRDATAD (TF_ERROR, ibc_hdc_info_data.error_reg, 8, "Taskfile Error Register"), },
|
||||
{ HRDATAD (TF_STATUS, ibc_hdc_info_data.status_reg, 8, "Taskfile Status Register"), },
|
||||
{ HRDATAD (TF_CSEC, ibc_hdc_info_data.taskfile[TF_CSEC], 8, "Taskfile Current Sector Register"), },
|
||||
{ HRDATAD (TF_HEAD, ibc_hdc_info_data.taskfile[TF_HEAD], 8, "Taskfile Current Head Register"), },
|
||||
{ HRDATAD (TF_NSEC, ibc_hdc_info_data.taskfile[TF_NSEC], 8, "Taskfile Sector Count Register"), },
|
||||
{ HRDATAD (TF_SA3, ibc_hdc_info_data.taskfile[TF_SA3], 8, "Taskfile SA3 Register"), },
|
||||
{ HRDATAD (TF_CMD, ibc_hdc_info_data.taskfile[TF_CMD], 8, "Taskfile Command Register"), },
|
||||
{ HRDATAD (TF_DRIVE, ibc_hdc_info_data.taskfile[TF_DRIVE], 8, "Taskfile Drive Register"), },
|
||||
{ HRDATAD (TF_TRKL, ibc_hdc_info_data.taskfile[TF_TRKL], 8, "Taskfile Track Low Register"), },
|
||||
{ HRDATAD (TF_TRKH, ibc_hdc_info_data.taskfile[TF_TRKH], 8, "Taskfile Track High Register"), },
|
||||
{ HRDATAD (TF_FIFO, ibc_hdc_info_data.taskfile[TF_FIFO], 8, "Data FIFO"), },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
#define IBC_HDC_NAME "IBC MCC ST-506 Hard Disk Controller"
|
||||
|
||||
static const char* ibc_hdc_description(DEVICE *dptr) {
|
||||
if (dptr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return IBC_HDC_NAME;
|
||||
}
|
||||
|
||||
static MTAB ibc_hdc_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_hdc_unit_set_geometry, &ibc_hdc_unit_show_geometry, NULL,
|
||||
"Set disk geometry C:nnnn/H:n/S:nnn/N:nnnn" },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
/* Debug Flags */
|
||||
static DEBTAB ibc_hdc_dt[] = {
|
||||
{ "ERROR", ERROR_MSG, "Error messages" },
|
||||
{ "CMD", CMD_MSG, "Command messages" },
|
||||
{ "READ", RD_DATA_MSG, "Read messages" },
|
||||
{ "WRITE", WR_DATA_MSG, "Write messages" },
|
||||
{ "FIFO", FIFO_MSG, "FIFO messages" },
|
||||
{ "TF", TF_MSG, "Taskfile messages" },
|
||||
{ "VERBOSE", VERBOSE_MSG, "Verbose messages" },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
DEVICE ibc_hdc_dev = {
|
||||
DEV_NAME, ibc_hdc_unit, ibc_hdc_reg, ibc_hdc_mod,
|
||||
IBC_HDC_MAX_DRIVES, 10, 31, 1, IBC_HDC_MAX_DRIVES, IBC_HDC_MAX_DRIVES,
|
||||
NULL, NULL, &ibc_hdc_reset,
|
||||
NULL, &ibc_hdc_attach, &ibc_hdc_detach,
|
||||
&ibc_hdc_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG,
|
||||
ibc_hdc_dt, NULL, NULL, NULL, NULL, NULL, &ibc_hdc_description
|
||||
};
|
||||
|
||||
/* Reset routine */
|
||||
static t_stat ibc_hdc_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, &ibchdcdev, "ibchdcdev", TRUE);
|
||||
} else {
|
||||
/* Connect IBC_HDC at base address */
|
||||
if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &ibchdcdev, "ibchdcdev", FALSE) != 0) {
|
||||
sim_printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base);
|
||||
return SCPE_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
ibc_hdc_info->status_reg = 0x80;
|
||||
ibc_hdc_info->error_reg = 0;
|
||||
ibc_hdc_info->sel_drive = 0;
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
|
||||
/* Attach routine */
|
||||
static t_stat ibc_hdc_attach(UNIT *uptr, CONST char *cptr)
|
||||
{
|
||||
t_stat r = SCPE_OK;
|
||||
IBC_HDC_DRIVE_INFO *pDrive;
|
||||
int i = 0;
|
||||
|
||||
i = find_unit_index(uptr);
|
||||
if (i == -1) {
|
||||
return (SCPE_IERR);
|
||||
}
|
||||
pDrive = &ibc_hdc_info->drive[i];
|
||||
|
||||
/* Defaults for the Quantum 2020 Drive */
|
||||
pDrive->ready = 0;
|
||||
if (pDrive->ncyls == 0) {
|
||||
/* If geometry was not specified, default to Quantun 2020 */
|
||||
pDrive->ncyls = 512;
|
||||
pDrive->nheads = 4;
|
||||
pDrive->nsectors = 32;
|
||||
pDrive->sectsize = 256;
|
||||
}
|
||||
|
||||
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_hdc_detach(uptr);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
sim_debug(VERBOSE_MSG, &ibc_hdc_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_hdc_info->error_reg = 0;
|
||||
pDrive->ready = 1;
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
|
||||
/* Detach routine */
|
||||
static t_stat ibc_hdc_detach(UNIT *uptr)
|
||||
{
|
||||
IBC_HDC_DRIVE_INFO *pDrive;
|
||||
t_stat r;
|
||||
int32 i;
|
||||
|
||||
i = find_unit_index(uptr);
|
||||
|
||||
if (i == -1) {
|
||||
return (SCPE_IERR);
|
||||
}
|
||||
|
||||
pDrive = &ibc_hdc_info->drive[i];
|
||||
|
||||
pDrive->ready = 0;
|
||||
|
||||
sim_debug(VERBOSE_MSG, &ibc_hdc_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_hdc_unit_set_geometry(UNIT* uptr, int32 value, CONST char* cptr, void* desc)
|
||||
{
|
||||
IBC_HDC_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_hdc_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_HDC_MAX_CYLS) {
|
||||
sim_debug(ERROR_MSG, &ibc_hdc_dev, DEV_NAME "%d: Number of cylinders must be 1-%d.\n",
|
||||
ibc_hdc_info->sel_drive, IBC_HDC_MAX_CYLS);
|
||||
return SCPE_ARG;
|
||||
}
|
||||
if (newHeads < 1 || newHeads > IBC_HDC_MAX_HEADS) {
|
||||
sim_debug(ERROR_MSG, &ibc_hdc_dev, DEV_NAME "%d: Number of heads must be 1-%d.\n",
|
||||
ibc_hdc_info->sel_drive, IBC_HDC_MAX_HEADS);
|
||||
return SCPE_ARG;
|
||||
}
|
||||
if (newSPT < 1 || newSPT > IBC_HDC_MAX_SPT) {
|
||||
sim_debug(ERROR_MSG, &ibc_hdc_dev, DEV_NAME "%d: Number of sectors per track must be 1-%d.\n",
|
||||
ibc_hdc_info->sel_drive, IBC_HDC_MAX_SPT);
|
||||
return SCPE_ARG;
|
||||
}
|
||||
if (newSecLen != 512 && newSecLen != 256 && newSecLen != 128) {
|
||||
sim_debug(ERROR_MSG, &ibc_hdc_dev,DEV_NAME "%d: Sector length must be 128, 256, or 512.\n",
|
||||
ibc_hdc_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_hdc_unit_show_geometry(FILE* st, UNIT* uptr, int32 value, CONST void* desc)
|
||||
{
|
||||
IBC_HDC_DRIVE_INFO* pDrive;
|
||||
int32 i;
|
||||
|
||||
i = find_unit_index(uptr);
|
||||
|
||||
if (i == -1) {
|
||||
return (SCPE_IERR);
|
||||
}
|
||||
|
||||
pDrive = &ibc_hdc_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 HDC I/O Dispatch */
|
||||
static int32 ibchdcdev(const int32 port, const int32 io, const int32 data)
|
||||
{
|
||||
if(io) {
|
||||
IBC_HDC_Write(port, (uint8)data);
|
||||
return 0;
|
||||
} else {
|
||||
return(IBC_HDC_Read(port));
|
||||
}
|
||||
}
|
||||
|
||||
/* I/O Write to IBC Disk Slave Task File */
|
||||
static uint8 IBC_HDC_Write(const uint32 Addr, uint8 cData)
|
||||
{
|
||||
switch(Addr) {
|
||||
case 0x40:
|
||||
ibc_hdc_info->reg_temp_holding[0] = cData;
|
||||
sim_debug(TF_MSG, &ibc_hdc_dev, DEV_NAME ": " ADDRESS_FORMAT
|
||||
" WR 0x%02x=0x%02x, HL=0x%04x\n", PCX, Addr, cData, HL_S);
|
||||
if (cData & 0x80) {
|
||||
ibc_hdc_info->taskfile[TF_CMD] = ibc_hdc_info->reg_temp_holding[0];
|
||||
ibc_hdc_info->taskfile[TF_DRIVE] = ibc_hdc_info->reg_temp_holding[1];
|
||||
ibc_hdc_info->taskfile[TF_TRKL] = ibc_hdc_info->reg_temp_holding[2];
|
||||
ibc_hdc_info->taskfile[TF_TRKH] = ibc_hdc_info->reg_temp_holding[3];
|
||||
if ((ibc_hdc_info->taskfile[TF_CMD] & 0x80) != IBC_HDC_CMD_READ_PARAMETERS) {
|
||||
ibc_hdc_info->sel_drive = ibc_hdc_info->taskfile[TF_DRIVE] & 0x03;
|
||||
}
|
||||
ibc_hdc_info->status_reg = 0x30;
|
||||
}
|
||||
else {
|
||||
ibc_hdc_info->taskfile[TF_CSEC] = ibc_hdc_info->reg_temp_holding[0];
|
||||
ibc_hdc_info->taskfile[TF_HEAD] = ibc_hdc_info->reg_temp_holding[1];
|
||||
ibc_hdc_info->taskfile[TF_NSEC] = ibc_hdc_info->reg_temp_holding[2];
|
||||
ibc_hdc_info->taskfile[TF_SA3] = ibc_hdc_info->reg_temp_holding[3];
|
||||
ibc_hdc_info->status_reg = 0x20;
|
||||
IBC_HDC_doCommand();
|
||||
}
|
||||
break;
|
||||
/* Fall through */
|
||||
case 0x41:
|
||||
case 0x42:
|
||||
case 0x43:
|
||||
ibc_hdc_info->reg_temp_holding[Addr & 0x03] = cData;
|
||||
sim_debug(TF_MSG, &ibc_hdc_dev, DEV_NAME ": " ADDRESS_FORMAT
|
||||
" WR 0x%02x=0x%02x, HL=0x%04x\n", PCX, Addr, cData, HL_S);
|
||||
break;
|
||||
case IBC_HDC_REG_FIFO_STATUS:
|
||||
ibc_hdc_info->secbuf_index = 0;
|
||||
break;
|
||||
case IBC_HDC_REG_FIFO:
|
||||
sim_debug(FIFO_MSG, &ibc_hdc_dev, DEV_NAME ": " ADDRESS_FORMAT
|
||||
" WR FIFO 0x%02x=0x%02x, HL=0x%04x\n", PCX, Addr, cData, HL_S);
|
||||
ibc_hdc_info->sectbuf[ibc_hdc_info->secbuf_index++] = cData;
|
||||
break;
|
||||
default:
|
||||
sim_debug(TF_MSG, &ibc_hdc_dev, DEV_NAME ": " ADDRESS_FORMAT
|
||||
" Unhandled WR 0x%02x=0x%02x\n", PCX, Addr, cData);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* I/O Read from IBC Disk Slave Task File */
|
||||
static uint8 IBC_HDC_Read(const uint32 Addr)
|
||||
{
|
||||
uint8 cData = 0xFF;
|
||||
|
||||
switch (Addr) {
|
||||
case IBC_HDC_REG_STATUS:
|
||||
cData = ibc_hdc_info->status_reg;
|
||||
sim_debug(TF_MSG, &ibc_hdc_dev,DEV_NAME ": " ADDRESS_FORMAT
|
||||
" RD TF[STATUS]=0x%02x\n", PCX, cData);
|
||||
break;
|
||||
case IBC_HDC_REG_FIFO:
|
||||
cData = ibc_hdc_info->sectbuf[ibc_hdc_info->secbuf_index];
|
||||
|
||||
sim_debug(FIFO_MSG, &ibc_hdc_dev, DEV_NAME ": " ADDRESS_FORMAT
|
||||
" RD TF[FIFO][0x%02x]=0x%02x\n", PCX, ibc_hdc_info->secbuf_index, cData);
|
||||
ibc_hdc_info->secbuf_index++;
|
||||
break;
|
||||
case IBC_HDC_REG_FIFO_STATUS:
|
||||
break;
|
||||
default:
|
||||
sim_debug(TF_MSG, &ibc_hdc_dev, DEV_NAME ": " ADDRESS_FORMAT
|
||||
" Unhandled RD 0x%02x=0x%02x\n", PCX, Addr, cData);
|
||||
break;
|
||||
}
|
||||
|
||||
return (cData);
|
||||
}
|
||||
|
||||
/* Validate that Cyl, Head, Sector, Sector Length are valid for the current
|
||||
* disk drive geometry.
|
||||
*/
|
||||
static int IBC_HDC_Validate_CHSN(IBC_HDC_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) ||
|
||||
(pDrive->cur_sectsize != pDrive->sectsize))
|
||||
{
|
||||
/* Set error bit in status register. */
|
||||
ibc_hdc_info->status_reg |= IBC_HDC_STATUS_ERROR;
|
||||
|
||||
/* Set ID_NOT_FOUND bit in error register. */
|
||||
ibc_hdc_info->error_reg |= IBC_HDC_ERROR_ID_NOT_FOUND;
|
||||
|
||||
sim_debug(ERROR_MSG, &ibc_hdc_dev,DEV_NAME "%d: " ADDRESS_FORMAT
|
||||
" C:%d/H:%d/S:%d/N:%d: ID Not Found (check disk geometry.)\n", ibc_hdc_info->sel_drive, PCX,
|
||||
pDrive->cur_cyl,
|
||||
pDrive->cur_head,
|
||||
pDrive->cur_sect,
|
||||
pDrive->cur_sectsize);
|
||||
|
||||
status = SCPE_IOERR;
|
||||
}
|
||||
else {
|
||||
/* Clear ID_NOT_FOUND bit in error register. */
|
||||
ibc_hdc_info->error_reg &= ~IBC_HDC_ERROR_ID_NOT_FOUND;
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
/* 85MB Fixed Disk Drive 0: C:680/H:15/N:32/L:256
|
||||
* 10MB Removable Cartridge Drive 3: C:612/H:2/N:32/L:256
|
||||
*/
|
||||
unsigned char HDParameters[108] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x00
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, // 0x08 0088=136
|
||||
0x00, 0x10, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, // 0x10 0110=272, 0198=408
|
||||
0x00, 0x20, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, // 0x18 0220=544
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x61, 0x62, // 0x20
|
||||
0x20, 0x00, 0x61, 0x02, 0x02, 0x00, 0x00, 0x00, // 0x28
|
||||
0x0F, 0x00, 0x88, 0x00, 0x20, 0x00, 0x1D, 0x03, // 0x30=#heads
|
||||
0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x38
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x40
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00, // 0x48
|
||||
0x61, 0x62, 0x20, 0x00, 0x61, 0x02, 0x02, 0x00, // 0x50
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x58
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x60
|
||||
0xFF, 0xFF, 0xFF, 0xFF // 0x68
|
||||
};
|
||||
|
||||
/* Perform IBC Disk Controller Command */
|
||||
static t_stat IBC_HDC_doCommand(void)
|
||||
{
|
||||
t_stat r = SCPE_OK;
|
||||
IBC_HDC_DRIVE_INFO* pDrive = &ibc_hdc_info->drive[ibc_hdc_info->sel_drive];
|
||||
uint8 cmd = ibc_hdc_info->taskfile[TF_CMD] & 0x7F;
|
||||
|
||||
pDrive->cur_cyl = ibc_hdc_info->taskfile[TF_TRKH] << 8;
|
||||
pDrive->cur_cyl |= ibc_hdc_info->taskfile[TF_TRKL];
|
||||
pDrive->xfr_nsects = ibc_hdc_info->taskfile[TF_NSEC];
|
||||
pDrive->cur_head = ibc_hdc_info->taskfile[TF_HEAD];
|
||||
pDrive->cur_sect = ibc_hdc_info->taskfile[TF_CSEC];
|
||||
pDrive->cur_sectsize = 256;
|
||||
if (pDrive->xfr_nsects == 0) {
|
||||
pDrive->xfr_nsects = 1;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case IBC_HDC_CMD_RESET: /* Reset */
|
||||
sim_debug(ERROR_MSG, &ibc_hdc_dev, DEV_NAME "%d: " ADDRESS_FORMAT
|
||||
" RESET COMMAND 0x%02x\n",
|
||||
ibc_hdc_info->sel_drive, PCX,
|
||||
cmd);
|
||||
ibc_hdc_info->status_reg = 0x20;
|
||||
break;
|
||||
case IBC_HDC_CMD_READ_SECT:
|
||||
case IBC_HDC_CMD_WRITE_SECT:
|
||||
{
|
||||
uint32 xfr_len;
|
||||
uint32 file_offset;
|
||||
|
||||
sim_debug(CMD_MSG, &ibc_hdc_dev, DEV_NAME "%d: " ADDRESS_FORMAT
|
||||
" CMD: %02x: Params 0x%02x,%02x,%02x - 0x%02x,%02x,%02x,%02x.\n", ibc_hdc_info->sel_drive, PCX,
|
||||
ibc_hdc_info->taskfile[TF_CMD], ibc_hdc_info->taskfile[TF_TRKH], ibc_hdc_info->taskfile[TF_TRKL], ibc_hdc_info->taskfile[TF_DRIVE],
|
||||
ibc_hdc_info->taskfile[TF_SA3], ibc_hdc_info->taskfile[TF_NSEC], ibc_hdc_info->taskfile[TF_HEAD], ibc_hdc_info->taskfile[TF_CSEC]);
|
||||
|
||||
/* Abort the read/write operation if C/H/S/N is not valid. */
|
||||
if (IBC_HDC_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->xfr_nsects * pDrive->sectsize;
|
||||
|
||||
if (0 != (r = sim_fseek((pDrive->uptr)->fileref, file_offset, SEEK_SET)))
|
||||
break;
|
||||
|
||||
if (cmd == IBC_HDC_CMD_READ_SECT) { /* Read */
|
||||
sim_debug(RD_DATA_MSG, &ibc_hdc_dev, DEV_NAME "%d: " ADDRESS_FORMAT
|
||||
" READ SECTOR C:%04d/H:%d/S:%04d/#:%d, offset=%5x, len=%d\n",
|
||||
ibc_hdc_info->sel_drive, PCX,
|
||||
pDrive->cur_cyl, pDrive->cur_head,
|
||||
pDrive->cur_sect, pDrive->xfr_nsects, file_offset, xfr_len);
|
||||
if (sim_fread(ibc_hdc_info->sectbuf, 1, xfr_len, (pDrive->uptr)->fileref) != xfr_len) {
|
||||
r = SCPE_IOERR;
|
||||
}
|
||||
ibc_hdc_info->status_reg = 0x60;
|
||||
}
|
||||
else { /* Write */
|
||||
sim_debug(WR_DATA_MSG, &ibc_hdc_dev, DEV_NAME "%d: " ADDRESS_FORMAT
|
||||
" WRITE SECTOR C:%04d/H:%d/S:%04d/#:%d, offset=%5x, len=%d\n",
|
||||
ibc_hdc_info->sel_drive, PCX,
|
||||
pDrive->cur_cyl, pDrive->cur_head,
|
||||
pDrive->cur_sect, pDrive->xfr_nsects, file_offset, xfr_len);
|
||||
|
||||
if (sim_fwrite(ibc_hdc_info->sectbuf, 1, xfr_len, (pDrive->uptr)->fileref) != xfr_len) {
|
||||
r = SCPE_IOERR;
|
||||
}
|
||||
|
||||
ibc_hdc_info->status_reg = 0x60;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IBC_HDC_CMD_FORMAT_TRK:
|
||||
{
|
||||
uint32 data_len;
|
||||
uint32 file_offset;
|
||||
uint8* fmtBuffer;
|
||||
|
||||
sim_debug(WR_DATA_MSG, &ibc_hdc_dev, DEV_NAME "%d: " ADDRESS_FORMAT
|
||||
" FORMAT TRACK C:%04d/H:%d\n",
|
||||
ibc_hdc_info->sel_drive, PCX,
|
||||
pDrive->cur_cyl, pDrive->cur_head);
|
||||
|
||||
data_len = pDrive->nsectors * pDrive->sectsize;
|
||||
|
||||
/* Abort the read/write operation if C/H/S/N is not valid. */
|
||||
if (IBC_HDC_Validate_CHSN(pDrive) != SCPE_OK) break;
|
||||
|
||||
sim_debug(WR_DATA_MSG, &ibc_hdc_dev, DEV_NAME "%d: " ADDRESS_FORMAT
|
||||
" FORMAT TRACK: C:%d/H:%d/Fill=0x%02x/Len=%d\n",
|
||||
ibc_hdc_info->sel_drive, PCX, pDrive->cur_cyl,
|
||||
pDrive->cur_head, IBC_HDC_FORMAT_FILL_BYTE, data_len);
|
||||
|
||||
/* Calculate file offset, formatting always handles a full track at a time. */
|
||||
file_offset = (pDrive->cur_cyl * pDrive->nheads * pDrive->nsectors); /* Full cylinders */
|
||||
file_offset += (pDrive->cur_head * pDrive->nsectors); /* Add full heads */
|
||||
file_offset *= pDrive->sectsize; /* Convert #sectors to byte offset */
|
||||
|
||||
fmtBuffer = calloc(data_len, sizeof(uint8));
|
||||
|
||||
if (fmtBuffer == 0) {
|
||||
return sim_messagef(SCPE_MEM, "Cannot allocate %d bytes for format buffer.\n", data_len);
|
||||
}
|
||||
|
||||
#if (IBC_HDC_FORMAT_FILL_BYTE != 0)
|
||||
memset(fmtBuffer, IBC_HDC_FORMAT_FILL_BYTE, data_len);
|
||||
#endif
|
||||
|
||||
if (0 != (r = sim_fseek((pDrive->uptr)->fileref, file_offset, SEEK_SET))) {
|
||||
if (sim_fwrite(fmtBuffer, 1, data_len, (pDrive->uptr)->fileref) != data_len) {
|
||||
r = SCPE_IOERR;
|
||||
}
|
||||
}
|
||||
|
||||
free(fmtBuffer);
|
||||
ibc_hdc_info->status_reg = 0x20;
|
||||
|
||||
break;
|
||||
}
|
||||
case IBC_HDC_CMD_ACCESS_FIFO: /* Access FIFO */
|
||||
sim_debug(WR_DATA_MSG, &ibc_hdc_dev, DEV_NAME "%d: " ADDRESS_FORMAT
|
||||
" ACCESS FIFO %d blocks.\n",
|
||||
ibc_hdc_info->sel_drive, PCX,
|
||||
ibc_hdc_info->taskfile[TF_NSEC]);
|
||||
ibc_hdc_info->secbuf_index = 0;
|
||||
ibc_hdc_info->status_reg = 0x20;
|
||||
break;
|
||||
case IBC_HDC_CMD_READ_PARAMETERS: /* Read Drive Parameters */
|
||||
sim_debug(ERROR_MSG, &ibc_hdc_dev, DEV_NAME "%d: " ADDRESS_FORMAT
|
||||
" READ DRIVE PARAMETERS C:%0d/H:%d/S:%2d\n",
|
||||
ibc_hdc_info->sel_drive, PCX,
|
||||
pDrive->cur_cyl, pDrive->cur_head, pDrive->cur_sect);
|
||||
memcpy(ibc_hdc_info->sectbuf, HDParameters, sizeof(HDParameters));
|
||||
ibc_hdc_info->status_reg = 0x60;
|
||||
break;
|
||||
default:
|
||||
sim_debug(ERROR_MSG, &ibc_hdc_dev, DEV_NAME "%d: " ADDRESS_FORMAT
|
||||
" UNKNOWN COMMAND 0x%02x\n",
|
||||
ibc_hdc_info->sel_drive, PCX,
|
||||
cmd);
|
||||
ibc_hdc_info->status_reg = 0x60;
|
||||
break;
|
||||
}
|
||||
return r;
|
||||
}
|
614
AltairZ80/ibc_smd_hdc.c
Normal file
614
AltairZ80/ibc_smd_hdc.c
Normal file
|
@ -0,0 +1,614 @@
|
|||
/*************************************************************************
|
||||
* *
|
||||
* 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 Quantun 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;
|
||||
}
|
|
@ -133,6 +133,9 @@ typedef struct {
|
|||
uint8 fdc_sec_len; /* N Sector Length */
|
||||
int8 step_dir;
|
||||
uint8 cmdtype; /* Type of current/former command */
|
||||
uint16 external_fifo_len;
|
||||
uint8 *external_fifo;
|
||||
uint16 fdc_fifo_index;
|
||||
WD179X_DRIVE_INFO drive[WD179X_MAX_DRIVES];
|
||||
} WD179X_INFO;
|
||||
|
||||
|
@ -199,7 +202,6 @@ static uint8 Do1793Command(uint8 cCommand);
|
|||
static t_stat wd179x_trackWrite(WD179X_DRIVE_INFO* pDrive, uint8 Cyl,
|
||||
uint8 Head, uint8 fillbyte, uint32* flags);
|
||||
|
||||
|
||||
WD179X_INFO wd179x_info_data = { { 0x0, 0, 0x30, 4 }, 1793, 0, 0 };
|
||||
WD179X_INFO *wd179x_info = &wd179x_info_data;
|
||||
WD179X_INFO_PUB *wd179x_infop = (WD179X_INFO_PUB *)&wd179x_info_data;
|
||||
|
@ -336,6 +338,18 @@ static t_stat wd179x_reset(DEVICE *dptr)
|
|||
return SCPE_OK;
|
||||
}
|
||||
|
||||
void wd179x_connect_external_fifo(uint16 fifo_len, uint8* storage)
|
||||
{
|
||||
wd179x_info->external_fifo_len = fifo_len;
|
||||
wd179x_info->external_fifo = storage;
|
||||
wd179x_info->fdc_fifo_index = 0;
|
||||
}
|
||||
|
||||
void wd179x_reset_external_fifo(void)
|
||||
{
|
||||
wd179x_info->fdc_fifo_index = 0;
|
||||
}
|
||||
|
||||
void wd179x_external_restore(void)
|
||||
{
|
||||
WD179X_DRIVE_INFO *pDrive;
|
||||
|
@ -613,12 +627,13 @@ uint8 WD179X_Read(const uint32 Addr)
|
|||
cData = 0xFF; /* Return High-Z data */
|
||||
if (wd179x_info->fdc_read == TRUE) {
|
||||
if (wd179x_info->fdc_dataindex < wd179x_info->fdc_datacount) {
|
||||
wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */
|
||||
cData = sdata.raw[wd179x_info->fdc_dataindex];
|
||||
if (wd179x_info->fdc_read_addr == TRUE) {
|
||||
sim_debug(STATUS_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
|
||||
" READ_ADDR[%d] = 0x%02x\n",
|
||||
" READ_ADDR[%d/%d] = 0x%02x\n",
|
||||
wd179x_info->sel_drive, PCX,
|
||||
wd179x_info->fdc_dataindex, cData);
|
||||
wd179x_info->fdc_dataindex, wd179x_info->fdc_datacount, cData);
|
||||
}
|
||||
|
||||
wd179x_info->fdc_dataindex++;
|
||||
|
@ -877,6 +892,7 @@ static uint8 Do1793Command(uint8 cCommand)
|
|||
WD179X_DRIVE_INFO *pDrive;
|
||||
uint32 flags = 0;
|
||||
uint32 readlen;
|
||||
uint32 writelen;
|
||||
|
||||
if (wd179x_info->sel_drive >= WD179X_MAX_DRIVES) {
|
||||
return 0xFF;
|
||||
|
@ -1073,6 +1089,37 @@ static uint8 Do1793Command(uint8 cCommand)
|
|||
wd179x_info->fdc_read_addr = FALSE;
|
||||
|
||||
sdata.raw[wd179x_info->fdc_dataindex] = wd179x_info->fdc_data;
|
||||
|
||||
if (wd179x_info->external_fifo_len) {
|
||||
/* For external FIFO, write the sector immediately, as the sofware pre-fills a FIFO, which is then read out into the FDC using DRQ */
|
||||
wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */
|
||||
wd179x_info->drq = 0;
|
||||
wd179x_info->intrq = 1;
|
||||
if (wd179x_info->intenable) {
|
||||
vectorInterrupt |= (1 << wd179x_info->intvector);
|
||||
dataBus[wd179x_info->intvector] = wd179x_info->intvector * 2;
|
||||
}
|
||||
|
||||
sim_debug(WR_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT
|
||||
" Writing sector, T:%2d/S:%d/N:%2d, Len=%d\n", wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->
|
||||
fdc_head, wd179x_info->fdc_sector, 128 << wd179x_info->fdc_sec_len);
|
||||
|
||||
/* Copy the data from the external FIFO */
|
||||
memcpy(sdata.raw, &wd179x_info->external_fifo[wd179x_info->fdc_fifo_index], 128 << wd179x_info->fdc_sec_len);
|
||||
wd179x_info->fdc_fifo_index += 128 << wd179x_info->fdc_sec_len;
|
||||
wd179x_info->fdc_fifo_index &= (wd179x_info->external_fifo_len - 1);
|
||||
|
||||
wd179x_sectWrite(pDrive,
|
||||
pDrive->track,
|
||||
wd179x_info->fdc_head,
|
||||
wd179x_info->fdc_sector,
|
||||
sdata.raw,
|
||||
128 << wd179x_info->fdc_sec_len,
|
||||
&flags,
|
||||
&writelen);
|
||||
|
||||
wd179x_info->fdc_write = FALSE;
|
||||
}
|
||||
break;
|
||||
/* Type III Commands */
|
||||
case WD179X_READ_ADDR:
|
||||
|
@ -1235,6 +1282,14 @@ static uint8 Do1793Command(uint8 cCommand)
|
|||
case WD179X_READ_ADDR:
|
||||
case WD179X_READ_TRACK:
|
||||
case WD179X_WRITE_TRACK:
|
||||
wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */
|
||||
if (wd179x_info->intenable) {
|
||||
wd179x_info->intrq = 1;
|
||||
vectorInterrupt |= (1 << wd179x_info->intvector);
|
||||
dataBus[wd179x_info->intvector] = wd179x_info->intvector * 2;
|
||||
}
|
||||
wd179x_info->drq = 1;
|
||||
break;
|
||||
/* Type IV Commands */
|
||||
case WD179X_FORCE_INTR:
|
||||
default:
|
||||
|
@ -1321,6 +1376,12 @@ done:
|
|||
wd179x_info->fdc_dataindex = 0;
|
||||
wd179x_info->fdc_read = TRUE;
|
||||
wd179x_info->fdc_read_addr = FALSE;
|
||||
if (wd179x_info->external_fifo_len) {
|
||||
/* Save the FDC data in the external FIFO */
|
||||
memcpy(&wd179x_info->external_fifo[wd179x_info->fdc_fifo_index], sdata.raw, 128 << wd179x_info->fdc_sec_len);
|
||||
wd179x_info->fdc_fifo_index += 128 << wd179x_info->fdc_sec_len;
|
||||
wd179x_info->fdc_fifo_index &= (wd179x_info->external_fifo_len - 1);
|
||||
}
|
||||
} else {
|
||||
wd179x_info->fdc_status = 0; /* Clear DRQ, BUSY */
|
||||
wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND;
|
||||
|
|
1
makefile
1
makefile
|
@ -1896,6 +1896,7 @@ ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \
|
|||
${ALTAIRZ80D}/flashwriter2.c ${ALTAIRZ80D}/i86_decode.c \
|
||||
${ALTAIRZ80D}/i86_ops.c ${ALTAIRZ80D}/i86_prim_ops.c \
|
||||
${ALTAIRZ80D}/i8272.c ${ALTAIRZ80D}/insnsd.c ${ALTAIRZ80D}/altairz80_mhdsk.c \
|
||||
${ALTAIRZ80D}/ibc.c ${ALTAIRZ80D}/ibc_mcc_hdc.c ${ALTAIRZ80D}/ibc_smd_hdc.c \
|
||||
${ALTAIRZ80D}/mfdc.c ${ALTAIRZ80D}/n8vem.c ${ALTAIRZ80D}/vfdhd.c \
|
||||
${ALTAIRZ80D}/s100_disk1a.c ${ALTAIRZ80D}/s100_disk2.c ${ALTAIRZ80D}/s100_disk3.c \
|
||||
${ALTAIRZ80D}/s100_fif.c ${ALTAIRZ80D}/s100_mdriveh.c \
|
||||
|
|
Loading…
Add table
Reference in a new issue