simh-testsetgenerator/AltairZ80/s100_mdsad.c
Mike Douglas f3af74e444 AltairZ80: Fix head stepping to only occur on 1 to 0 transitions
The source file to be updated provides emulation of the North Star DD
floppy disk controller. The proposed change steps the head only when the
step bit in the order byte transitions from 1 to 0. This is the way the
actual hardware functions. Previously, the emulation would step any time
an order was issued and the step bit was 1. This behavior caused some
original software (e.g., Lifeboat CP/M 2.2) to end up on an incorrect
track.
2015-08-08 21:27:09 -05:00

784 lines
34 KiB
C

/*************************************************************************
* *
* $Id: s100_mdsad.c 1995 2008-07-15 03:59:13Z hharte $ *
* *
* Copyright (c) 2007-2008 Howard M. Harte. *
* http://www.hartetec.com *
* *
* 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 *
* NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE 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 name of Howard M. Harte shall *
* not be used in advertising or otherwise to promote the sale, use or *
* other dealings in this Software without prior written authorization *
* Howard M. Harte. *
* *
* SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. *
* *
* Module Description: *
* Northstar MDS-AD Disk Controller module for SIMH *
* Only Double-Density is supported for now. *
* *
* Environment: *
* User mode only *
* *
*************************************************************************/
/*#define DBG_MSG*/
#include "altairz80_defs.h"
#include "sim_imd.h"
#if defined (_WIN32)
#include <windows.h>
#endif
#ifdef DBG_MSG
#define DBG_PRINT(args) sim_printf args
#else
#define DBG_PRINT(args)
#endif
/* Debug flags */
#define ERROR_MSG (1 << 0)
#define SEEK_MSG (1 << 1)
#define CMD_MSG (1 << 2)
#define RD_DATA_MSG (1 << 3)
#define WR_DATA_MSG (1 << 4)
#define STATUS_MSG (1 << 5)
#define ORDERS_MSG (1 << 6)
#define RD_DATA_DETAIL_MSG (1 << 7)
#define WR_DATA_DETAIL_MSG (1 << 8)
extern uint32 PCX;
extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc);
extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc);
extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type,
int32 (*routine)(const int32, const int32, const int32), uint8 unmap);
#define MDSAD_MAX_DRIVES 4
#define MDSAD_SECTOR_LEN 512
#define MDSAD_SECTORS_PER_TRACK 10
#define MDSAD_TRACKS 35
#define MDSAD_RAW_LEN (32 + 2 + MDSAD_SECTOR_LEN + 1)
typedef union {
struct {
uint8 zeros[32];
uint8 sync[2];
uint8 data[MDSAD_SECTOR_LEN];
uint8 checksum;
} u;
uint8 raw[MDSAD_RAW_LEN];
} SECTOR_FORMAT;
typedef struct {
UNIT *uptr;
uint8 track;
uint8 wp; /* Disk write protected */
uint8 sector; /* Current Sector number */
uint32 sector_wait_count;
} MDSAD_DRIVE_INFO;
typedef struct {
uint8 dd; /* Controls density on write DD=1 for double density and DD=0 for single density. */
uint8 ss; /* Specifies the side of a double-sided diskette. The bottom side (and only side of a single-sided diskette) is selected when SS=0. The second (top) side is selected when SS=1. */
uint8 dp; /* has shared use. During stepping operations, DP=O specifies a step out and DP=1 specifies a step in. During write operations, write procompensation is invoked if and only if DP=1. */
uint8 st; /* controls the level of the head step signal to the disk drives. */
uint8 pst; /* value of step signal (st) on previous order */
uint8 ds; /* is the drive select field, encoded as follows: */
/* 0=no drive selected
* 1=drive 1 selected
* 2=drive 2 selected
* 4=drive 3 selected
* 8=drive 4 selected
*/
} ORDERS;
typedef struct {
uint8 sf; /* Sector Flag: set when sector hole detected, reset by software. */
uint8 ix; /* Index Detect: true if index hole detected during previous sector. */
uint8 dd; /* Double Density Indicator: true if data being read is encoded in double density. */
uint8 mo; /* Motor On: true while motor(s) are on. */
} COM_STATUS;
typedef struct {
uint8 wi; /* Window: true during 96-microsecond window at beginning of sector. */
uint8 re; /* Read Enable: true while phase-locked loop is enabled. */
uint8 sp; /* Spare: reserved for future use. */
uint8 bd; /* Body: set when sync character is detected. */
} A_STATUS;
typedef struct {
uint8 wr; /* Write: true during valid write operation. */
uint8 sp; /* Spare: reserved for future use. */
uint8 wp; /* Write Protect: true while the diskette installed in the selected drive is write protected. */
uint8 t0; /* Track 0: true if selected drive is at track zero. */
} B_STATUS;
typedef struct {
uint8 sc; /* Sector Counter: indicates the current sector position. */
} C_STATUS;
typedef struct {
PNP_INFO pnp; /* Plug and Play */
ORDERS orders;
COM_STATUS com_status;
A_STATUS a_status;
B_STATUS b_status;
C_STATUS c_status;
uint8 int_enable; /* Interrupt Enable */
uint32 datacount; /* Number of data bytes transferred from controller for current sector */
MDSAD_DRIVE_INFO drive[MDSAD_MAX_DRIVES];
} MDSAD_INFO;
static MDSAD_INFO mdsad_info_data = { { 0xE800, 1024, 0, 0 } };
static MDSAD_INFO *mdsad_info = &mdsad_info_data;
static SECTOR_FORMAT sdata;
#define UNIT_V_MDSAD_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
#define UNIT_MDSAD_VERBOSE (1 << UNIT_V_MDSAD_VERBOSE)
#define MDSAD_CAPACITY (70*10*MDSAD_SECTOR_LEN) /* Default North Star Disk Capacity */
/* MDS-AD Controller Subcases */
#define MDSAD_READ_ROM 0
#define MDSAD_WRITE_DATA 1
#define MDSAD_CTLR_ORDERS 2
#define MDSAD_CTLR_COMMAND 3
/* MDS-AD Controller Commands */
#define MDSAD_CMD_NOP 0
#define MDSAD_CMD_RESET_SF 1
#define MDSAD_CMD_INTR_DIS 2
#define MDSAD_CMD_INTR_ARM 3
#define MDSAD_CMD_SET_BODY 4
#define MDSAD_CMD_MOTORS_ON 5
#define MDSAD_CMD_BEGIN_WR 6
#define MDSAD_CMD_RESET 7
/* MDS-AD Data returned on DI bus */
#define MDSAD_A_STATUS 1
#define MDSAD_B_STATUS 2
#define MDSAD_C_STATUS 3
#define MDSAD_READ_DATA 4
/* MDS-AD status byte masks */
/* A-Status */
#define MDSAD_A_SF 0x80
#define MDSAD_A_IX 0x40
#define MDSAD_A_DD 0x20
#define MDSAD_A_MO 0x10
#define MDSAD_A_WI 0x08
#define MDSAD_A_RE 0x04
#define MDSAD_A_SP 0x02
#define MDSAD_A_BD 0x01
/* B-Status */
#define MDSAD_B_SF 0x80
#define MDSAD_B_IX 0x40
#define MDSAD_B_DD 0x20
#define MDSAD_B_MO 0x10
#define MDSAD_B_WR 0x08
#define MDSAD_B_SP 0x04
#define MDSAD_B_WP 0x02
#define MDSAD_B_T0 0x01
/* C-Status */
#define MDSAD_C_SF 0x80
#define MDSAD_C_IX 0x40
#define MDSAD_C_DD 0x20
#define MDSAD_C_MO 0x10
#define MDSAD_C_SC 0x0f
/* Local function prototypes */
static t_stat mdsad_reset(DEVICE *mdsad_dev);
static t_stat mdsad_attach(UNIT *uptr, char *cptr);
static t_stat mdsad_detach(UNIT *uptr);
static t_stat mdsad_boot(int32 unitno, DEVICE *dptr);
static uint8 MDSAD_Read(const uint32 Addr);
static int32 mdsaddev(const int32 Addr, const int32 rw, const int32 data);
static UNIT mdsad_unit[] = {
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MDSAD_CAPACITY) },
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MDSAD_CAPACITY) },
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MDSAD_CAPACITY) },
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MDSAD_CAPACITY) }
};
static REG mdsad_reg[] = {
{ NULL }
};
#define MDSAD_NAME "North Star Floppy Controller MDSAD"
static MTAB mdsad_mod[] = {
{ MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE",
&set_membase, &show_membase, NULL, "Sets disk controller memory base address" },
/* quiet, no warning messages */
{ UNIT_MDSAD_VERBOSE, 0, "QUIET", "QUIET",
NULL, NULL, NULL, "No verbose messages for unit " MDSAD_NAME "n" },
/* verbose, show warning messages */
{ UNIT_MDSAD_VERBOSE, UNIT_MDSAD_VERBOSE, "VERBOSE", "VERBOSE",
NULL, NULL, NULL, "Verbose messages for unit " MDSAD_NAME "n" },
{ 0 }
};
/* Debug Flags */
static DEBTAB mdsad_dt[] = {
{ "ERROR", ERROR_MSG, "Error messages" },
{ "SEEK", SEEK_MSG, "Seek messages" },
{ "CMD", CMD_MSG, "Command messages" },
{ "READ", RD_DATA_MSG, "Read messages" },
{ "WRITE", WR_DATA_MSG, "Write messages" },
{ "STATUS", STATUS_MSG, "Status messages" },
{ "ORDERS", ORDERS_MSG, "Orders messages" },
{ "RDDETAIL", RD_DATA_DETAIL_MSG, "Read detail messages" },
{ "WRDETAIL", WR_DATA_DETAIL_MSG, "Write detail messags" },
{ NULL, 0 }
};
DEVICE mdsad_dev = {
"MDSAD", mdsad_unit, mdsad_reg, mdsad_mod,
MDSAD_MAX_DRIVES, 10, 31, 1, MDSAD_MAX_DRIVES, MDSAD_MAX_DRIVES,
NULL, NULL, &mdsad_reset,
&mdsad_boot, &mdsad_attach, &mdsad_detach,
&mdsad_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG,
mdsad_dt, NULL, "North Star Floppy Controller MDSAD"
};
/* Reset routine */
static t_stat mdsad_reset(DEVICE *dptr)
{
PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt;
if(dptr->flags & DEV_DIS) {
sim_map_resource(pnp->mem_base, pnp->mem_size,
RESOURCE_TYPE_MEMORY, &mdsaddev, TRUE);
} else {
/* Connect MDSAD at base address */
if(sim_map_resource(pnp->mem_base, pnp->mem_size,
RESOURCE_TYPE_MEMORY, &mdsaddev, FALSE) != 0) {
sim_printf("%s: error mapping resource at 0x%04x\n",
__FUNCTION__, pnp->mem_base);
dptr->flags |= DEV_DIS;
return SCPE_ARG;
}
}
return SCPE_OK;
}
/* Attach routine */
static t_stat mdsad_attach(UNIT *uptr, char *cptr)
{
char header[4];
t_stat r;
unsigned int i = 0;
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 = MDSAD_CAPACITY;
}
for(i = 0; i < MDSAD_MAX_DRIVES; i++) {
mdsad_info->drive[i].uptr = &mdsad_dev.units[i];
}
for(i = 0; i < MDSAD_MAX_DRIVES; i++) {
if(mdsad_dev.units[i].fileref == uptr->fileref) {
break;
}
mdsad_info->orders.st = 0; /* ensure valid state */
}
/* Default for new file is DSK */
uptr->u3 = IMAGE_TYPE_DSK;
if(uptr->capac > 0) {
char *rtn = fgets(header, 4, uptr->fileref);
if((rtn != NULL) && (strncmp(header, "CPT", 3) == 0)) {
sim_printf("CPT images not yet supported\n");
uptr->u3 = IMAGE_TYPE_CPT;
mdsad_detach(uptr);
return SCPE_OPENERR;
} else {
uptr->u3 = IMAGE_TYPE_DSK;
}
}
if (uptr->flags & UNIT_MDSAD_VERBOSE)
sim_printf("MDSAD%d, attached to '%s', type=%s, len=%d\n", i, cptr,
uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK",
uptr->capac);
return SCPE_OK;
}
/* Detach routine */
static t_stat mdsad_detach(UNIT *uptr)
{
t_stat r;
int8 i;
for(i = 0; i < MDSAD_MAX_DRIVES; i++) {
if(mdsad_dev.units[i].fileref == uptr->fileref) {
break;
}
}
if (i >= MDSAD_MAX_DRIVES)
return SCPE_ARG;
DBG_PRINT(("Detach MDSAD%d\n", i));
r = detach_unit(uptr); /* detach unit */
if(r != SCPE_OK)
return r;
mdsad_dev.units[i].fileref = NULL;
return SCPE_OK;
}
static t_stat mdsad_boot(int32 unitno, DEVICE *dptr)
{
PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt;
DBG_PRINT(("Booting MDSAD Controller at 0x%04x, unit %d" NLP,
pnp->mem_base+1+(unitno&3), unitno & 3));
/* Unit 3 can't be booted yet. This involves modifying the A register. */
*((int32 *) sim_PC->loc) = pnp->mem_base+1+(unitno&3);
return SCPE_OK;
}
static int32 mdsaddev(const int32 Addr, const int32 rw, const int32 data)
{
if(rw == 0) { /* Read */
return(MDSAD_Read(Addr));
} else { /* Write */
DBG_PRINT(("MDSAD: write attempt at 0x%04x ignored." NLP, Addr));
return (-1);
}
}
/* This ROM image is taken from the Solace Emulator, which uses */
/* a ROM from a "Micro Complex Phase Lock II" dual- */
/* density controller card. It is supposedly compatible with the */
/* Northstar-designed dual density floppy controller. It has the */
/* interesting property that by jumping to base_addr+0 (or +1) and */
/* it boots from floppy 0; jump to base_addr+2 you boot from floppy 1; */
/* jump to base_addr+3 and you boot from floppy 2. You can boot from */
/* floppy 3 by loading A with 08H and jumping to base_addr+7. */
static uint8 mdsad_rom[] = {
0x44, 0x01, 0x01, 0x01, 0x82, 0x84, 0x78, 0xE6, 0x07, 0x4F, 0x00, 0x31, 0x30, 0x00, 0x21, 0x29, /* 0x00 */
0x00, 0xE5, 0x21, 0x2C, 0xC2, 0xE5, 0x21, 0x77, 0x13, 0xE5, 0x21, 0xC9, 0x1A, 0xE5, 0xCD, 0x28, /* 0x10 */
0x00, 0x21, 0x30, 0x00, 0x5B, 0x52, 0x44, 0x54, 0x5D, 0x3A, 0x27, 0x00, 0x57, 0xC3, 0x29, 0x00, /* 0x20 */
0x14, 0x14, 0x1E, 0x15, 0x1A, 0x26, 0x30, 0xCD, 0xD9, 0x00, 0x42, 0x05, 0x0A, 0xCD, 0xD4, 0x00, /* 0x30 */
0x2E, 0x0D, 0x2D, 0xCA, 0x43, 0x00, 0xCD, 0xD7, 0x00, 0x1A, 0xE6, 0x40, 0xCA, 0x42, 0x00, 0x3E, /* 0x40 */
0x0A, 0xF5, 0xCD, 0xC1, 0x00, 0x1E, 0x20, 0x1A, 0xE6, 0x01, 0xC2, 0x63, 0x00, 0xCD, 0xC5, 0x00, /* 0x50 */
0xC3, 0x55, 0x00, 0x2E, 0x04, 0xCD, 0xE7, 0x00, 0x1E, 0x10, 0x1A, 0xE6, 0x04, 0xCA, 0x68, 0x00, /* 0x60 */
0x3E, 0x09, 0x3D, 0xC2, 0x72, 0x00, 0x1A, 0xE6, 0x20, 0xC2, 0x84, 0x00, 0xCD, 0xC1, 0x00, 0x2E, /* 0x70 */
0x08, 0xCD, 0xE7, 0x00, 0x06, 0xA3, 0x1E, 0x10, 0x05, 0xCA, 0xF4, 0x00, 0x1A, 0x0F, 0xD2, 0x88, /* 0x80 */
0x00, 0x1E, 0x40, 0x1A, 0x67, 0x2E, 0x00, 0x36, 0x59, 0x07, 0x47, 0x23, 0x1A, 0x77, 0xA8, 0x07, /* 0x90 */
0x47, 0x2C, 0xC2, 0x9C, 0x00, 0x24, 0x1A, 0x77, 0xA8, 0x07, 0x47, 0x2C, 0xC2, 0xA6, 0x00, 0x1A, /* 0xA0 */
0xA8, 0xC2, 0xF4, 0x00, 0x25, 0x2E, 0x03, 0x71, 0x2D, 0x36, 0x59, 0xC2, 0xB8, 0x00, 0x2E, 0x0A, /* 0xB0 */
0xE9, 0x3E, 0x20, 0x81, 0x4F, 0x0A, 0x3E, 0x10, 0x81, 0x4F, 0x0A, 0x3E, 0xF0, 0x81, 0x4F, 0x0A, /* 0xC0 */
0x79, 0xE6, 0x0F, 0x4F, 0xCD, 0xD7, 0x00, 0x26, 0x01, 0x1E, 0x11, 0x1A, 0x1D, 0x1A, 0xB7, 0xF2, /* 0xD0 */
0xDD, 0x00, 0x25, 0xC2, 0xD9, 0x00, 0xC9, 0xCD, 0xD7, 0x00, 0x1E, 0x35, 0x1A, 0xE6, 0x0F, 0xBD, /* 0xE0 */
0xC2, 0xE7, 0x00, 0xC9, 0xF1, 0x3D, 0xF5, 0xC2, 0x55, 0x00, 0xC3, 0xFA, 0x00, 0x52, 0x44, 0x54 /* 0xF0 */
};
static void showdata(int32 isRead) {
int32 i;
sim_printf("MDSAD: " ADDRESS_FORMAT " %s Sector =" NLP "\t", PCX, isRead ? "Read" : "Write");
for(i=0; i < MDSAD_SECTOR_LEN; i++) {
sim_printf("%02X ", sdata.u.data[i]);
if(((i+1) & 0xf) == 0)
sim_printf(NLP "\t");
}
sim_printf(NLP);
}
static int checksum;
static uint32 sec_offset;
static uint32 calculate_mdsad_sec_offset(uint8 track, uint8 head, uint8 sector)
{
if(mdsad_info->orders.ss == 0) {
return ((track * (MDSAD_SECTOR_LEN * MDSAD_SECTORS_PER_TRACK)) + (sector * MDSAD_SECTOR_LEN));
} else {
return ((((MDSAD_TRACKS-1) - track) * (MDSAD_SECTOR_LEN * MDSAD_SECTORS_PER_TRACK)) +
((MDSAD_SECTOR_LEN * MDSAD_SECTORS_PER_TRACK) * MDSAD_TRACKS) + /* Skip over side 0 */
(sector * MDSAD_SECTOR_LEN)); /* Sector offset from beginning of track. */
}
}
static uint8 MDSAD_Read(const uint32 Addr)
{
uint8 cData;
uint8 ds;
MDSAD_DRIVE_INFO *pDrive;
int32 rtn;
cData = 0x00;
pDrive = &mdsad_info->drive[mdsad_info->orders.ds];
switch( (Addr & 0x300) >> 8 ) {
case MDSAD_READ_ROM:
cData = mdsad_rom[Addr & 0xFF];
break;
case MDSAD_WRITE_DATA:
{
if(mdsad_info->datacount == 0) {
sim_debug(WR_DATA_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" WRITE Start: Drive: %d, Track=%d, Head=%d, Sector=%d\n",
PCX, mdsad_info->orders.ds, pDrive->track,
mdsad_info->orders.ss, pDrive->sector);
sec_offset = calculate_mdsad_sec_offset(pDrive->track,
mdsad_info->orders.ss,
pDrive->sector);
}
DBG_PRINT(("MDSAD: " ADDRESS_FORMAT
" WRITE-DATA[offset:%06x+%03x]=%02x" NLP,
PCX, sec_offset, mdsad_info->datacount, Addr & 0xFF));
mdsad_info->datacount++;
if(mdsad_info->datacount < MDSAD_RAW_LEN)
sdata.raw[mdsad_info->datacount] = Addr & 0xFF;
if(mdsad_info->datacount == (MDSAD_RAW_LEN - 1)) {
sim_debug(WR_DATA_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" Write Complete\n", PCX);
if ((pDrive->uptr == NULL) || (pDrive->uptr->fileref == NULL)) {
sim_debug(WR_DATA_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" Drive: %d not attached - write ignored.\n", PCX, mdsad_info->orders.ds);
return 0x00;
}
if(mdsad_dev.dctrl & WR_DATA_DETAIL_MSG)
showdata(FALSE);
switch((pDrive->uptr)->u3)
{
case IMAGE_TYPE_DSK:
if(pDrive->uptr->fileref == NULL) {
sim_printf(".fileref is NULL!" NLP);
} else {
sim_fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET);
sim_fwrite(sdata.u.data, 1, MDSAD_SECTOR_LEN,
(pDrive->uptr)->fileref);
}
break;
case IMAGE_TYPE_CPT:
sim_printf("%s: CPT Format not supported" NLP, __FUNCTION__);
break;
default:
sim_printf("%s: Unknown image Format" NLP, __FUNCTION__);
break;
}
}
break;
}
case MDSAD_CTLR_ORDERS:
mdsad_info->orders.dd = (Addr & 0x80) >> 7;
mdsad_info->orders.ss = (Addr & 0x40) >> 6;
mdsad_info->orders.dp = (Addr & 0x20) >> 5;
mdsad_info->orders.pst = mdsad_info->orders.st; /* save previous state of st */
mdsad_info->orders.st = (Addr & 0x10) >> 4;
mdsad_info->orders.ds = (Addr & 0x0F);
ds = mdsad_info->orders.ds;
switch(mdsad_info->orders.ds) {
case 0:
case 1:
mdsad_info->orders.ds = 0;
break;
case 2:
mdsad_info->orders.ds = 1;
break;
case 4:
mdsad_info->orders.ds = 2;
break;
case 8:
mdsad_info->orders.ds = 3;
break;
}
if(mdsad_info->orders.ds != (mdsad_info->orders.ds & 0x03)) {
sim_debug(ERROR_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" Controller Orders update drive %x\n", PCX, mdsad_info->orders.ds);
mdsad_info->orders.ds &= 0x03;
}
sim_debug(ORDERS_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" Controller Orders: Drive=%x[%x], DD=%d, SS=%d, DP=%d, ST=%d\n",
PCX, mdsad_info->orders.ds, ds, mdsad_info->orders.dd,
mdsad_info->orders.ss, mdsad_info->orders.dp, mdsad_info->orders.st);
/* use latest selected drive */
pDrive = &mdsad_info->drive[mdsad_info->orders.ds];
/* step only on transition of the step bit from 1 to 0. This duplicates the
way the hardware functions and is required by some North Star code */
if((mdsad_info->orders.st == 0) && (mdsad_info->orders.pst != 0)) {
if(mdsad_info->orders.dp == 0) {
sim_debug(SEEK_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" Step out: Track=%d%s\n", PCX, pDrive->track,
pDrive->track == 0 ? "[Warn: already at 0]" : "");
if(pDrive->track > 0) /* anything to do? */
pDrive->track--;
} else {
sim_debug(SEEK_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" Step in: Track=%d%s\n", PCX, pDrive->track,
pDrive->track == (MDSAD_TRACKS - 1) ? "[Warn: already at highest track]" : "");
if(pDrive->track < (MDSAD_TRACKS - 1)) /* anything to do? */
pDrive->track++;
}
}
/* always update t0 */
mdsad_info->b_status.t0 = (pDrive->track == 0);
break;
case MDSAD_CTLR_COMMAND:
/* sim_debug(CMD_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT " DM=%x\n", PCX, (Addr & 0xF0) >> 4); */
switch(Addr & 0x0F) {
case MDSAD_CMD_MOTORS_ON:
sim_debug(CMD_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" CMD=Motors On\n", PCX);
mdsad_info->com_status.mo = 1; /* Turn motors on */
break;
case MDSAD_CMD_NOP:
pDrive->sector_wait_count++;
switch(pDrive->sector_wait_count) {
case 10:
{
mdsad_info->com_status.sf = 1;
mdsad_info->a_status.wi = 0;
mdsad_info->a_status.re = 0;
mdsad_info->a_status.bd = 0;
pDrive->sector_wait_count = 0;
pDrive->sector++;
if(pDrive->sector >= MDSAD_SECTORS_PER_TRACK) {
pDrive->sector = 0;
mdsad_info->com_status.ix = 1;
} else {
mdsad_info->com_status.ix = 0;
}
break;
}
case 2:
mdsad_info->a_status.wi = 1;
break;
case 3:
mdsad_info->a_status.re = 1;
mdsad_info->a_status.bd = 1;
break;
default:
break;
}
break;
case MDSAD_CMD_RESET_SF:
sim_debug(CMD_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" CMD=Reset Sector Flag\n", PCX);
mdsad_info->com_status.sf = 0;
mdsad_info->datacount = 0;
break;
case MDSAD_CMD_INTR_DIS:
sim_debug(CMD_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" CMD=Disarm Interrupt\n", PCX);
mdsad_info->int_enable = 0;
break;
case MDSAD_CMD_INTR_ARM:
sim_debug(CMD_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" CMD=Arm Interrupt\n", PCX);
mdsad_info->int_enable = 1;
break;
case MDSAD_CMD_SET_BODY:
sim_debug(CMD_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" CMD=Set Body (Diagnostic)\n", PCX);
break;
case MDSAD_CMD_BEGIN_WR:
sim_debug(CMD_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" CMD=Begin Write\n", PCX);
break;
case MDSAD_CMD_RESET:
sim_debug(CMD_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" CMD=Reset Controller\n", PCX);
mdsad_info->com_status.mo = 0; /* Turn motors off */
break;
default:
sim_debug(CMD_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" Unsupported CMD=0x%x\n", PCX, Addr & 0x0F);
break;
}
/* Always Double-Density for now... */
mdsad_info->com_status.dd = 1;
cData = (mdsad_info->com_status.sf & 1) << 7;
cData |= (mdsad_info->com_status.ix & 1) << 6;
cData |= (mdsad_info->com_status.dd & 1) << 5;
cData |= (mdsad_info->com_status.mo & 1) << 4;
mdsad_info->c_status.sc = pDrive->sector;
switch( (Addr & 0xF0) >> 4) {
case MDSAD_A_STATUS: /* A-STATUS */
cData |= (mdsad_info->a_status.wi & 1) << 3;
cData |= (mdsad_info->a_status.re & 1) << 2;
cData |= (mdsad_info->a_status.sp & 1) << 1;
cData |= (mdsad_info->a_status.bd & 1);
sim_debug(STATUS_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" A-Status = <%s %s %s %s %s %s %s %s>\n",
PCX,
cData & MDSAD_A_SF ? "SF" : " ",
cData & MDSAD_A_IX ? "IX" : " ",
cData & MDSAD_A_DD ? "DD" : " ",
cData & MDSAD_A_MO ? "MO" : " ",
cData & MDSAD_A_WI ? "WI" : " ",
cData & MDSAD_A_RE ? "RE" : " ",
cData & MDSAD_A_SP ? "SP" : " ",
cData & MDSAD_A_BD ? "BD" : " ");
break;
case MDSAD_B_STATUS: /* B-STATUS */
cData |= (mdsad_info->b_status.wr & 1) << 3;
cData |= (mdsad_info->b_status.sp & 1) << 2;
cData |= (mdsad_info->b_status.wp & 1) << 1;
cData |= (mdsad_info->b_status.t0 & 1);
sim_debug(STATUS_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" B-Status = <%s %s %s %s %s %s %s %s>\n",
PCX,
cData & MDSAD_B_SF ? "SF" : " ",
cData & MDSAD_B_IX ? "IX" : " ",
cData & MDSAD_B_DD ? "DD" : " ",
cData & MDSAD_B_MO ? "MO" : " ",
cData & MDSAD_B_WR ? "WR" : " ",
cData & MDSAD_B_SP ? "SP" : " ",
cData & MDSAD_B_WP ? "WP" : " ",
cData & MDSAD_B_T0 ? "T0" : " ");
break;
case MDSAD_C_STATUS: /* C-STATUS */
cData |= (mdsad_info->c_status.sc & 0xF);
sim_debug(STATUS_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" C-Status = <%s %s %s %s %i>\n",
PCX,
cData & MDSAD_C_SF ? "SF" : " ",
cData & MDSAD_C_IX ? "IX" : " ",
cData & MDSAD_C_DD ? "DD" : " ",
cData & MDSAD_C_MO ? "MO" : " ",
cData & MDSAD_C_SC);
break;
case MDSAD_READ_DATA: /* READ DATA */
{
if(mdsad_info->datacount == 0) {
sim_debug(RD_DATA_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" READ Start: Drive: %d, Track=%d, Head=%d, Sector=%d\n",
PCX,
mdsad_info->orders.ds,
pDrive->track,
mdsad_info->orders.ss,
pDrive->sector);
checksum = 0;
sec_offset = calculate_mdsad_sec_offset(pDrive->track,
mdsad_info->orders.ss,
pDrive->sector);
if ((pDrive->uptr == NULL) ||
(pDrive->uptr->fileref == NULL)) {
sim_debug(RD_DATA_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" Drive: %d not attached - read ignored.\n",
PCX, mdsad_info->orders.ds);
return 0xe5;
}
switch((pDrive->uptr)->u3)
{
case IMAGE_TYPE_DSK:
if(pDrive->uptr->fileref == NULL) {
sim_printf(".fileref is NULL!" NLP);
} else {
sim_fseek((pDrive->uptr)->fileref,
sec_offset, SEEK_SET);
rtn = sim_fread(&sdata.u.data[0], 1, MDSAD_SECTOR_LEN,
(pDrive->uptr)->fileref);
if (rtn != MDSAD_SECTOR_LEN) {
sim_debug(ERROR_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" READ: sim_fread error.\n", PCX);
}
}
break;
case IMAGE_TYPE_CPT:
sim_printf("%s: CPT Format not supported"
NLP, __FUNCTION__);
break;
default:
sim_printf("%s: Unknown image Format"
NLP, __FUNCTION__);
break;
}
if(mdsad_dev.dctrl & RD_DATA_DETAIL_MSG)
showdata(TRUE);
}
if(mdsad_info->datacount < MDSAD_SECTOR_LEN) {
cData = sdata.u.data[mdsad_info->datacount];
/* Exclusive OR */
checksum ^= cData;
/* Rotate Left Circular */
checksum = ((checksum << 1) | ((checksum & 0x80) != 0)) & 0xff;
DBG_PRINT(("MDSAD: " ADDRESS_FORMAT
" READ-DATA[offset:%06x+%03x]=%02x" NLP,
PCX, sec_offset, mdsad_info->datacount, cData));
} else { /* checksum */
cData = checksum;
sim_debug(RD_DATA_MSG, &mdsad_dev, "MDSAD: " ADDRESS_FORMAT
" READ-DATA: Checksum is: 0x%02x\n",
PCX, cData);
}
mdsad_info->datacount++;
break;
}
default:
DBG_PRINT(("MDSAD: " ADDRESS_FORMAT
" Invalid DM=%x" NLP, PCX, Addr & 0xF));
break;
}
break;
}
return (cData);
}