1
0
mirror of https://github.com/simh/simh.git synced 2026-01-21 10:12:56 +00:00
Mark Pizzolato 66c264d678 RC2 of 3.9
After a frantic week of file exchanges, here is RC2.  A lot has changed.

1. HP2100 updates completed, with new features and peripherals.
2. Many, many small file updates for nits found by compilers and static checkers.
3. A few genuine bugs fixed.
4. New makefile and MMS file.

A note on the makefile. It has almost doubled in size and attempts to ferret out PCAP locales properly, as well as do serious optimizations if gcc is used.  It needs to be tested in more environments.  If you run into issues, please report them to Mark Pizzolato as well as me.

The old makefile, updated for the extra files in the HP2100, is included as makefile_old.  You can use that if the new makefile causes problems in your environment.

I'm still targeting a May Day release, with a final RC around Tax Day (April 15).  That leaves times for one more interim RC, if needed.

At this point, I regard the release as feature-complete.  Bug fixes are still fine.

The actual release will have all incomplete and beta simulators in a separate zip file, including Alpha, Sigma, Sage (the microcomputers, not the 50s anti-aircraft computer), and SC1, the SiCortex MIPS simulator.
There will be new releases of all the simulation tools as well.

/Bob
2012-03-24 15:30:27 -07:00

973 lines
46 KiB
C

/*************************************************************************
* *
* $Id: i8272.c 1999 2008-07-22 04:25:28Z 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: *
* Generic Intel 8272 Disk Controller module for SIMH. *
* *
* Environment: *
* User mode only *
* *
*************************************************************************/
/* Change log:
- 19-Apr-2008, Tony Nicholson, added other .IMD formats
- 06-Aug-2008, Tony Nicholson, READID should use HDS bit and add support
for logical Head and Cylinder maps in the .IMD image file (AGN)
*/
/*#define DBG_MSG */
#include "altairz80_defs.h"
#if defined (_WIN32)
#include <windows.h>
#endif
#include "sim_imd.h"
#include "i8272.h"
#ifdef DBG_MSG
#define DBG_PRINT(args) 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 FMT_MSG (1 << 6)
#define VERBOSE_MSG (1 << 7)
#define IRQ_MSG (1 << 8)
#define I8272_MAX_DRIVES 4
#define I8272_MAX_SECTOR 26
#define I8272_SECTOR_LEN 8192
/* 2^(7 + I8272_MAX_N) == I8272_SECTOR_LEN */
#define I8272_MAX_N 6
#define CMD_PHASE 0
#define EXEC_PHASE 1
#define DATA_PHASE 2
typedef union {
uint8 raw[I8272_SECTOR_LEN];
} SECTOR_FORMAT;
typedef struct {
UNIT *uptr;
DISK_INFO *imd;
uint8 ntracks; /* number of tracks */
uint8 nheads; /* number of heads */
uint32 sectsize; /* sector size, not including pre/postamble */
uint8 track; /* Current Track */
uint8 ready; /* Is drive ready? */
} I8272_DRIVE_INFO;
typedef struct {
PNP_INFO pnp; /* Plug-n-Play Information */
uint32 fdc_dma_addr;/* DMA Transfer Address */
uint8 fdc_msr; /* 8272 Main Status Register */
uint8 fdc_phase; /* Phase that the 8272 is currently in */
uint8 fdc_srt; /* Step Rate in ms */
uint8 fdc_hut; /* Head Unload Time in ms */
uint8 fdc_hlt; /* Head Load Time in ms */
uint8 fdc_nd; /* Non-DMA Mode 1=Non-DMA, 0=DMA */
uint8 fdc_head; /* H Head Number */
uint8 fdc_sector; /* R Record (Sector) */
uint8 fdc_sec_len; /* N Sector Length, in bytes: 2^(7 + fdc_sec_len), fdc_sec_len <= I8272_MAX_N */
uint8 fdc_eot; /* EOT End of Track (Final sector number of cyl) */
uint8 fdc_gpl; /* GPL Gap3 Length */
uint8 fdc_dtl; /* DTL Data Length */
uint8 fdc_mt; /* Multiple sectors */
uint8 fdc_mfm; /* MFM mode */
uint8 fdc_sk; /* Skip Deleted Data */
uint8 fdc_hds; /* Head Select */
uint8 fdc_fillbyte; /* Fill-byte used for FORMAT TRACK */
uint8 fdc_sc; /* Sector count for FORMAT TRACK */
uint8 fdc_sectorcount; /* Current sector being formatted by FORMAT TRACK */
uint8 fdc_sectormap[I8272_MAX_SECTOR]; /* Physical to logical sector map for FORMAT TRACK */
uint8 fdc_status[3];/* Status Register Bytes */
uint8 fdc_seek_end; /* Seek was executed successfully */
uint8 cmd_index; /* Index of command byte */
uint8 cmd[10]; /* Storage for current command */
uint8 cmd_len; /* FDC Command Length */
uint8 result_index; /* Index of result byte */
uint8 result[10]; /* Result data */
uint8 result_len; /* FDC Result Length */
uint8 sel_drive; /* Currently selected drive */
I8272_DRIVE_INFO drive[I8272_MAX_DRIVES];
} I8272_INFO;
static SECTOR_FORMAT sdata;
extern uint32 PCX;
extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc);
extern t_stat show_iobase(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);
/* These are needed for DMA. PIO Mode has not been implemented yet. */
extern void PutByteDMA(const uint32 Addr, const uint32 Value);
extern uint8 GetByteDMA(const uint32 Addr);
#define UNIT_V_I8272_WLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_I8272_WLK (1 << UNIT_V_I8272_WLK)
#define UNIT_V_I8272_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
#define UNIT_I8272_VERBOSE (1 << UNIT_V_I8272_VERBOSE)
#define I8272_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */
#define I8272_CAPACITY_SSSD (77*1*26*128) /* Single-sided Single Density IBM Diskette1 */
/* Intel 8272 Commands */
#define I8272_READ_TRACK 0x02
#define I8272_SPECIFY 0x03
#define I8272_SENSE_DRIVE_STATUS 0x04
#define I8272_WRITE_DATA 0x05
#define I8272_READ_DATA 0x06
#define I8272_RECALIBRATE 0x07
#define I8272_SENSE_INTR_STATUS 0x08
#define I8272_WRITE_DELETED_DATA 0x09
#define I8272_READ_ID 0x0A
#define I8272_READ_DELETED_DATA 0x0C
#define I8272_FORMAT_TRACK 0x0D
#define I8272_SEEK 0x0F
#define I8272_SCAN_EQUAL 0x11
#define I8272_SCAN_LOW_EQUAL 0x19
#define I8272_SCAN_HIGH_EQUAL 0x1D
/* SENSE DRIVE STATUS bit definitions */
#define DRIVE_STATUS_TWO_SIDED 0x08
#define DRIVE_STATUS_TRACK0 0x10
#define DRIVE_STATUS_READY 0x20
#define DRIVE_STATUS_WP 0x40
#define DRIVE_STATUS_FAULT 0x80
static void raise_i8272_interrupt(void);
static int32 i8272dev(const int32 port, const int32 io, const int32 data);
static t_stat i8272_reset(DEVICE *dptr);
int32 find_unit_index (UNIT *uptr);
I8272_INFO i8272_info_data = { { 0x0, 0, 0xC0, 2 } };
I8272_INFO *i8272_info = &i8272_info_data;
uint8 i8272_irq = 1;
static UNIT i8272_unit[] = {
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, I8272_CAPACITY) },
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, I8272_CAPACITY) },
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, I8272_CAPACITY) },
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, I8272_CAPACITY) }
};
static MTAB i8272_mod[] = {
{ MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL },
{ UNIT_I8272_WLK, 0, "WRTENB", "WRTENB", NULL },
{ UNIT_I8272_WLK, UNIT_I8272_WLK, "WRTLCK", "WRTLCK", NULL },
/* quiet, no warning messages */
{ UNIT_I8272_VERBOSE, 0, "QUIET", "QUIET", NULL },
/* verbose, show warning messages */
{ UNIT_I8272_VERBOSE, UNIT_I8272_VERBOSE, "VERBOSE", "VERBOSE", NULL },
{ 0 }
};
/* Debug Flags */
static DEBTAB i8272_dt[] = {
{ "ERROR", ERROR_MSG },
{ "SEEK", SEEK_MSG },
{ "CMD", CMD_MSG },
{ "RDDATA", RD_DATA_MSG },
{ "WRDATA", WR_DATA_MSG },
{ "STATUS", STATUS_MSG },
{ "FMT", FMT_MSG },
{ "VERBOSE",VERBOSE_MSG },
{ "IRQ", IRQ_MSG },
{ NULL, 0 }
};
DEVICE i8272_dev = {
"I8272", i8272_unit, NULL, i8272_mod,
I8272_MAX_DRIVES, 10, 31, 1, I8272_MAX_DRIVES, I8272_MAX_DRIVES,
NULL, NULL, &i8272_reset,
NULL, &i8272_attach, &i8272_detach,
&i8272_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG,
i8272_dt, NULL, "Intel/NEC(765) FDC Core I8272"
};
static uint8 I8272_Setup_Cmd(uint8 fdc_cmd);
/* Reset routine */
static t_stat i8272_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, &i8272dev, TRUE);
} else {
/* Connect I/O Ports at base address */
if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &i8272dev, FALSE) != 0) {
printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base);
return SCPE_ARG;
}
}
return SCPE_OK;
}
/* find_unit_index find index of a unit
Inputs:
uptr = pointer to unit
Outputs:
result = index of device
*/
int32 find_unit_index (UNIT *uptr)
{
DEVICE *dptr;
uint32 i;
if (uptr == NULL)
return (-1);
dptr = find_dev_from_unit(uptr);
for(i=0; i<dptr->numunits; i++) {
if(dptr->units + i == uptr) {
break;
}
}
if(i == dptr->numunits) {
return (-1);
}
return (i);
}
/* Attach routine */
t_stat i8272_attach(UNIT *uptr, char *cptr)
{
char header[4];
t_stat r;
int32 i = 0;
r = attach_unit(uptr, cptr); /* attach unit */
if ( r != SCPE_OK) /* error? */
return r;
/* Determine length of this disk */
uptr->capac = sim_fsize(uptr->fileref);
i = find_unit_index(uptr);
if (i == -1) {
return (SCPE_IERR);
}
DBG_PRINT(("Attach I8272%d\n", i));
i8272_info->drive[i].uptr = uptr;
/* Default to drive not ready */
i8272_info->drive[i].ready = 0;
if(uptr->capac > 0) {
char *rtn = fgets(header, 4, uptr->fileref);
if((rtn != NULL) && strncmp(header, "IMD", 3)) {
printf("I8272: Only IMD disk images are supported\n");
i8272_info->drive[i].uptr = NULL;
return SCPE_OPENERR;
}
} else {
/* create a disk image file in IMD format. */
if (diskCreate(uptr->fileref, "$Id: i8272.c 1999 2008-07-22 04:25:28Z hharte $") != SCPE_OK) {
printf("I8272: Failed to create IMD disk.\n");
i8272_info->drive[i].uptr = NULL;
return SCPE_OPENERR;
}
uptr->capac = sim_fsize(uptr->fileref);
}
uptr->u3 = IMAGE_TYPE_IMD;
if (uptr->flags & UNIT_I8272_VERBOSE) {
printf("I8272%d: attached to '%s', type=%s, len=%d\n", i, cptr,
uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK",
uptr->capac);
}
if(uptr->u3 == IMAGE_TYPE_IMD) {
if (uptr->flags & UNIT_I8272_VERBOSE)
printf("--------------------------------------------------------\n");
i8272_info->drive[i].imd = diskOpen(uptr->fileref, uptr->flags & UNIT_I8272_VERBOSE);
if (uptr->flags & UNIT_I8272_VERBOSE)
printf("\n");
if (i8272_info->drive[i].imd == NULL) {
printf("I8272: IMD disk corrupt.\n");
i8272_info->drive[i].uptr = NULL;
return SCPE_OPENERR;
}
i8272_info->drive[i].ready = 1;
} else {
i8272_info->drive[i].imd = NULL;
}
return SCPE_OK;
}
/* Detach routine */
t_stat i8272_detach(UNIT *uptr)
{
t_stat r;
int8 i;
i = find_unit_index(uptr);
if (i == -1) {
return (SCPE_IERR);
}
DBG_PRINT(("Detach I8272%d\n", i));
r = diskClose(&i8272_info->drive[i].imd);
i8272_info->drive[i].ready = 0;
if (r != SCPE_OK)
return r;
r = detach_unit(uptr); /* detach unit */
if (r != SCPE_OK)
return r;
return SCPE_OK;
}
static int32 i8272dev(const int32 port, const int32 io, const int32 data)
{
DBG_PRINT(("I8272: " ADDRESS_FORMAT " %s, Port 0x%02x Data 0x%02x" NLP,
PCX, io ? "OUT" : " IN", port, data));
if(io) {
I8272_Write(port, data);
return 0;
} else {
return(I8272_Read(port));
}
}
uint8 I8272_Set_DMA(const uint32 dma_addr)
{
i8272_info->fdc_dma_addr = dma_addr & 0xFFFFFF;
return 0;
}
extern uint8 floorlog2(unsigned int n);
#define I8272_MSR_RQM (1 << 7)
#define I8272_MSR_DATA_OUT (1 << 6)
#define I8272_MSR_NON_DMA (1 << 5)
#define I8272_MSR_FDC_BUSY (1 << 4)
uint8 I8272_Read(const uint32 Addr)
{
uint8 cData;
I8272_DRIVE_INFO *pDrive;
pDrive = &i8272_info->drive[i8272_info->sel_drive];
if(pDrive->uptr == NULL) {
return 0xFF;
}
/* the switch statement ensures that cData is set in all cases! */
switch(Addr & 0x3) {
case I8272_FDC_MSR:
cData = i8272_info->fdc_msr | I8272_MSR_RQM;
if(i8272_info->fdc_phase == CMD_PHASE) {
cData &= ~I8272_MSR_DATA_OUT;
} else {
cData |= I8272_MSR_DATA_OUT;
}
#if 0
if(i8272_info->fdc_phase == EXEC_PHASE) {
cData |= I8272_MSR_FDC_BUSY;
} else {
cData |= ~I8272_MSR_FDC_BUSY;
}
#endif /* 0 hharte */
sim_debug(STATUS_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" RD FDC MSR = 0x%02x\n", PCX, cData);
break;
case I8272_FDC_DATA:
if(i8272_info->fdc_phase == DATA_PHASE) {
cData = i8272_info->result[i8272_info->result_index];
sim_debug(VERBOSE_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" RD Data, phase=%d, [%d]=0x%02x\n",
PCX, i8272_info->fdc_phase, i8272_info->result_index, cData);
i8272_irq = 0;
i8272_info->result_index ++;
if(i8272_info->result_index == i8272_info->result_len) {
sim_debug(VERBOSE_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" result phase complete.\n", PCX);
i8272_info->fdc_phase = CMD_PHASE;
}
} else {
cData = i8272_info->result[0]; /* hack, in theory any value should be ok but this makes "format" work */
sim_debug(VERBOSE_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" error, reading data register when not in data phase. "
"Returning 0x%02x\n", PCX, cData);
}
break;
default:
sim_debug(VERBOSE_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Cannot read register %x\n", PCX, Addr);
cData = 0xFF;
}
return (cData);
}
static char *messages[0x20] = {
/* 0 1 2 3 */
"Undefined Command 0x0","Undefined Command 0x1","Read Track", "Specify",
/* 4 5 6 7 */
"Sense Drive Status", "Write Data", "Read Data", "Recalibrate",
/* 8 9 A B */
"Sense Interrupt Status", "Write Deleted Data", "Read ID", "Undefined Command 0xB",
/* C D E F */
"Read Deleted Data", "Format Track", "Undefined Command 0xE","Seek",
/* 10 11 12 13 */
"Undefined Command 0x10","Scan Equal", "Undefined Command 0x12","Undefined Command 0x13",
/* 14 15 16 17 */
"Undefined Command 0x14","Undefined Command 0x15","Undefined Command 0x16","Undefined Command 0x17",
/* 18 19 1A 1B */
"Undefined Command 0x18","Scan Low Equal", "Undefined Command 0x1A","Undefined Command 0x1B",
/* 1C 1D 1E 1F */
"Undefined Command 0x1C","Scan High Equal", "Undefined Command 0x1E","Undefined Command 0x1F"
};
uint8 I8272_Write(const uint32 Addr, uint8 cData)
{
I8272_DRIVE_INFO *pDrive;
uint32 flags = 0;
uint32 readlen;
uint8 disk_read = 0;
int32 i;
pDrive = &i8272_info->drive[i8272_info->sel_drive];
if(pDrive->uptr == NULL) {
return 0xFF;
}
switch(Addr & 0x3) {
case I8272_FDC_MSR:
sim_debug(WR_DATA_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" WR Drive Select Reg=%02x\n", PCX, cData);
break;
case I8272_FDC_DATA:
i8272_info->fdc_msr &= 0xF0;
sim_debug(VERBOSE_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" WR Data, phase=%d, index=%d\n",
PCX, i8272_info->fdc_phase, i8272_info->cmd_index);
if(i8272_info->fdc_phase == CMD_PHASE) {
i8272_info->cmd[i8272_info->cmd_index] = cData;
if(i8272_info->cmd_index == 0) {
sim_debug(CMD_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" CMD=0x%02x[%s]\n", PCX, cData & 0x1F, messages[cData & 0x1F]);
I8272_Setup_Cmd(cData & 0x1F);
}
i8272_info->cmd_index ++;
if(i8272_info->cmd_len == i8272_info->cmd_index) {
i8272_info->cmd_index = 0;
i8272_info->fdc_phase = EXEC_PHASE;
}
}
if(i8272_info->fdc_phase == EXEC_PHASE) {
switch(i8272_info->cmd[0] & 0x1F) {
case I8272_READ_DATA:
case I8272_WRITE_DATA:
case I8272_READ_DELETED_DATA:
case I8272_WRITE_DELETED_DATA:
case I8272_READ_TRACK:
case I8272_SCAN_LOW_EQUAL:
case I8272_SCAN_HIGH_EQUAL:
case I8272_SCAN_EQUAL:
i8272_info->fdc_mt = (i8272_info->cmd[0] & 0x80) >> 7;
i8272_info->fdc_mfm = (i8272_info->cmd[0] & 0x40) >> 6;
i8272_info->fdc_sk = (i8272_info->cmd[0] & 0x20) >> 5;
i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2;
i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03);
pDrive = &i8272_info->drive[i8272_info->sel_drive];
if(pDrive->uptr == NULL) {
return 0xFF;
}
if(pDrive->track != i8272_info->cmd[2]) {
i8272_info->fdc_seek_end = 1;
} else {
i8272_info->fdc_seek_end = 0;
}
if(pDrive->track != i8272_info->cmd[2]) {
sim_debug(CMD_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" ERROR: CMD=0x%02x[%s]: Drive: %d, Command wants track %d, "
"but positioner is on track %d.\n",
PCX, i8272_info->cmd[0] & 0x1F,
messages[i8272_info->cmd[0] & 0x1F],
i8272_info->sel_drive, i8272_info->cmd[2], pDrive->track);
}
pDrive->track = i8272_info->cmd[2];
i8272_info->fdc_head = i8272_info->cmd[3] & 1; /* AGN mask to head 0 or 1 */
i8272_info->fdc_sector = i8272_info->cmd[4];
i8272_info->fdc_sec_len = i8272_info->cmd[5];
if(i8272_info->fdc_sec_len > I8272_MAX_N) {
sim_debug(ERROR_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Illegal sector size %d [N=%d]. Reset to %d [N=%d].\n",
PCX, 128 << i8272_info->fdc_sec_len,
i8272_info->fdc_sec_len, 128 << I8272_MAX_N, I8272_MAX_N);
i8272_info->fdc_sec_len = I8272_MAX_N;
}
i8272_info->fdc_eot = i8272_info->cmd[6];
i8272_info->fdc_gpl = i8272_info->cmd[7];
i8272_info->fdc_dtl = i8272_info->cmd[8];
sim_debug(CMD_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" CMD=0x%02x[%s]: Drive: %d, %s %s, C=%d. H=%d. S=%d, N=%d, "
"EOT=%02x, GPL=%02x, DTL=%02x\n", PCX,
i8272_info->cmd[0] & 0x1F,
messages[i8272_info->cmd[0] & 0x1F],
i8272_info->sel_drive,
i8272_info->fdc_mt ? "Multi" : "Single",
i8272_info->fdc_mfm ? "MFM" : "FM",
pDrive->track,
i8272_info->fdc_head,
i8272_info->fdc_sector,
i8272_info->fdc_sec_len,
i8272_info->fdc_eot,
i8272_info->fdc_gpl,
i8272_info->fdc_dtl);
i8272_info->fdc_status[0] = (i8272_info->fdc_hds & 1) << 2;
i8272_info->fdc_status[0] |= (i8272_info->sel_drive & 0x03);
i8272_info->fdc_status[0] |= 0x40;
i8272_info->fdc_status[1] = 0;
i8272_info->fdc_status[2] = 0;
i8272_info->result[0] = i8272_info->fdc_status[0];
i8272_info->result[1] = i8272_info->fdc_status[1];
i8272_info->result[2] = i8272_info->fdc_status[2];
i8272_info->result[3] = pDrive->imd->track[pDrive->track][i8272_info->fdc_head].logicalCyl[i8272_info->fdc_sector]; /* AGN logicalCyl */
i8272_info->result[4] = pDrive->imd->track[pDrive->track][i8272_info->fdc_head].logicalHead[i8272_info->fdc_sector]; /* AGN logicalHead */
i8272_info->result[5] = i8272_info->fdc_sector;
i8272_info->result[6] = i8272_info->fdc_sec_len;
break;
case I8272_READ_ID: /* READ ID */
i8272_info->fdc_mfm = (i8272_info->cmd[0] & 0x40) >> 6;
i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2;
i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03);
pDrive = &i8272_info->drive[i8272_info->sel_drive];
if(pDrive->uptr == NULL) {
return 0xFF;
}
/* Compute the i8272 "N" value from the sectorsize of this */
/* disk's current track - i.e. N = log2(sectsize) - log2(128) */
/* The calculation also works for non-standard format disk images with */
/* sectorsizes of 2048, 4096 and 8192 bytes */
i8272_info->fdc_sec_len = floorlog2(
pDrive->imd->track[pDrive->track][i8272_info->fdc_hds].sectsize) - 7; /* AGN fix to use fdc_hds (was fdc_head)*/
/* For now always return the starting sector number */
/* but could return (say) a valid sector number based */
/* on elapsed time for a more "realistic" simulation. */
/* This would allow disk analysis programs that use */
/* READID to detect non-standard disk formats. */
i8272_info->fdc_sector = pDrive->imd->track[pDrive->track][i8272_info->fdc_hds].start_sector;
if((i8272_info->fdc_sec_len == 0xF8) || (i8272_info->fdc_sec_len > I8272_MAX_N)) { /* Error calculating N or N too large */
sim_debug(ERROR_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Illegal sector size N=%d. Reset to 0.\n",
PCX, i8272_info->fdc_sec_len);
i8272_info->fdc_sec_len = 0;
return 0xFF;
}
i8272_info->fdc_status[0] = (i8272_info->fdc_hds & 1) << 2;
i8272_info->fdc_status[0] |= (i8272_info->sel_drive & 0x03);
i8272_info->fdc_status[1] = 0;
i8272_info->fdc_status[2] = 0;
i8272_info->result[0] = i8272_info->fdc_status[0];
i8272_info->result[1] = i8272_info->fdc_status[1];
i8272_info->result[2] = i8272_info->fdc_status[2];
i8272_info->result[3] = pDrive->imd->track[pDrive->track][i8272_info->fdc_hds].logicalCyl[i8272_info->fdc_sector]; /* AGN logicalCyl */
i8272_info->result[4] = pDrive->imd->track[pDrive->track][i8272_info->fdc_hds].logicalHead[i8272_info->fdc_sector]; /* AGN logicalHead */
i8272_info->result[5] = i8272_info->fdc_sector;
i8272_info->result[6] = i8272_info->fdc_sec_len;
break;
case I8272_RECALIBRATE: /* RECALIBRATE */
i8272_info->sel_drive = i8272_info->cmd[1] & 0x03;
pDrive = &i8272_info->drive[i8272_info->sel_drive];
if(pDrive->uptr == NULL) {
return 0xFF;
}
pDrive->track = 0;
i8272_info->fdc_phase = CMD_PHASE; /* No result phase */
i8272_info->fdc_seek_end = 1;
sim_debug(SEEK_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Recalibrate: Drive 0x%02x\n",
PCX, i8272_info->sel_drive);
break;
case I8272_FORMAT_TRACK: /* FORMAT A TRACK */
i8272_info->fdc_mfm = (i8272_info->cmd[0] & 0x40) >> 6;
i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2;
i8272_info->fdc_head = i8272_info->fdc_hds;
i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03);
pDrive = &i8272_info->drive[i8272_info->sel_drive];
if(pDrive->uptr == NULL) {
return 0xFF;
}
if(pDrive->track != i8272_info->cmd[2]) {
i8272_info->fdc_seek_end = 1;
} else {
i8272_info->fdc_seek_end = 0;
}
i8272_info->fdc_sec_len = i8272_info->cmd[2];
if(i8272_info->fdc_sec_len > I8272_MAX_N) {
sim_debug(ERROR_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Illegal sector size %d [N=%d]. Reset to %d [N=%d].\n",
PCX, 128 << i8272_info->fdc_sec_len,
i8272_info->fdc_sec_len, 128 << I8272_MAX_N, I8272_MAX_N);
i8272_info->fdc_sec_len = I8272_MAX_N;
}
i8272_info->fdc_sc = i8272_info->cmd[3];
i8272_info->fdc_gpl = i8272_info->cmd[4];
i8272_info->fdc_fillbyte = i8272_info->cmd[5];
sim_debug(FMT_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Format Drive: %d, %s, C=%d. H=%d. N=%d, SC=%d, GPL=%02x, FILL=%02x\n",
PCX,
i8272_info->sel_drive,
i8272_info->fdc_mfm ? "MFM" : "FM",
pDrive->track,
i8272_info->fdc_head,
i8272_info->fdc_sec_len,
i8272_info->fdc_sc,
i8272_info->fdc_gpl,
i8272_info->fdc_fillbyte);
i8272_info->fdc_status[0] = (i8272_info->fdc_hds & 1) << 2;
i8272_info->fdc_status[0] |= (i8272_info->sel_drive & 0x03);
i8272_info->fdc_status[1] = 0;
i8272_info->fdc_status[2] = 0;
i8272_info->fdc_sectorcount = 0;
i8272_info->result[0] = i8272_info->fdc_status[0];
i8272_info->result[1] = i8272_info->fdc_status[1];
i8272_info->result[2] = i8272_info->fdc_status[2];
i8272_info->result[3] = pDrive->track;
i8272_info->result[4] = i8272_info->fdc_head; /* AGN for now we cannot format with logicalHead */
i8272_info->result[5] = i8272_info->fdc_sector; /* AGN ditto for logicalCyl */
i8272_info->result[6] = i8272_info->fdc_sec_len;
break;
case I8272_SENSE_INTR_STATUS: /* SENSE INTERRUPT STATUS */
sim_debug(CMD_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Sense Interrupt Status\n", PCX);
i8272_info->result[0] = i8272_info->fdc_seek_end ? 0x20 : 0x00; /* SEEK_END */
i8272_info->result[0] |= i8272_info->sel_drive;
i8272_info->result[1] = pDrive->track;
i8272_irq = 0;
break;
case I8272_SPECIFY: /* SPECIFY */
i8272_info->fdc_srt = 16 - ((i8272_info->cmd[1] & 0xF0) >> 4);
i8272_info->fdc_hut = (i8272_info->cmd[1] & 0x0F) * 16;
i8272_info->fdc_hlt = ((i8272_info->cmd[2] & 0xFE) >> 1) * 2;
i8272_info->fdc_nd = (i8272_info->cmd[2] & 0x01);
i8272_info->fdc_phase = CMD_PHASE; /* No result phase */
sim_debug(CMD_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Specify: SRT=%d, HUT=%d, HLT=%d, ND=%s\n",
PCX, i8272_info->fdc_srt,
i8272_info->fdc_hut,
i8272_info->fdc_hlt,
i8272_info->fdc_nd ? "NON-DMA" : "DMA");
break;
case I8272_SENSE_DRIVE_STATUS: /* Setup Status3 Byte */
i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2;
i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03);
pDrive = &i8272_info->drive[i8272_info->sel_drive];
if(pDrive->uptr == NULL) {
return 0xFF;
}
i8272_info->result[0] = (pDrive->ready) ? DRIVE_STATUS_READY : 0; /* Drive Ready */
if(imdGetSides(pDrive->imd) == 2) {
i8272_info->result[0] |= DRIVE_STATUS_TWO_SIDED; /* Two-sided? */
}
if(imdIsWriteLocked(pDrive->imd)) {
i8272_info->result[0] |= DRIVE_STATUS_WP; /* Write Protected? */
}
i8272_info->result[0] |= (i8272_info->fdc_hds & 1) << 2;
i8272_info->result[0] |= (i8272_info->sel_drive & 0x03);
i8272_info->result[0] |= (pDrive->track == 0) ? DRIVE_STATUS_TRACK0 : 0x00; /* Track 0 */
sim_debug(CMD_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Sense Drive Status = 0x%02x\n", PCX, i8272_info->result[0]);
break;
case I8272_SEEK: /* SEEK */
i8272_info->fdc_mt = (i8272_info->cmd[0] & 0x80) >> 7;
i8272_info->fdc_mfm = (i8272_info->cmd[0] & 0x40) >> 6;
i8272_info->fdc_sk = (i8272_info->cmd[0] & 0x20) >> 5;
i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2;
i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03);
pDrive = &i8272_info->drive[i8272_info->sel_drive];
if(pDrive->uptr == NULL) {
return 0xFF;
}
pDrive->track = i8272_info->cmd[2];
i8272_info->fdc_head = i8272_info->fdc_hds; /*AGN seek should save the head */
i8272_info->fdc_seek_end = 1;
sim_debug(SEEK_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Seek Drive: %d, %s %s, C=%d. Skip Deleted Data=%s Head Select=%s\n",
PCX,
i8272_info->sel_drive,
i8272_info->fdc_mt ? "Multi" : "Single",
i8272_info->fdc_mfm ? "MFM" : "FM",
i8272_info->cmd[2],
i8272_info->fdc_sk ? "True" : "False",
i8272_info->fdc_hds ? "True" : "False");
break;
default: /* INVALID */
break;
}
if(i8272_info->fdc_phase == EXEC_PHASE) {
switch(i8272_info->cmd[0] & 0x1F) {
case I8272_READ_TRACK:
printf("I8272: " ADDRESS_FORMAT " Read a track (untested.)" NLP, PCX);
i8272_info->fdc_sector = 1; /* Read entire track from sector 1...eot */
case I8272_READ_DATA:
case I8272_READ_DELETED_DATA:
disk_read = 1;
case I8272_WRITE_DATA:
case I8272_WRITE_DELETED_DATA:
for(;i8272_info->fdc_sector<=i8272_info->fdc_eot;i8272_info->fdc_sector++) {
sim_debug(RD_DATA_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" %s Data, sector: %d sector len=%d\n",
PCX, disk_read ? "RD" : "WR",
i8272_info->fdc_sector,
128 << i8272_info->fdc_sec_len);
if(pDrive->imd == NULL) {
printf(".imd is NULL!" NLP);
}
if(disk_read) { /* Read sector */
sectRead(pDrive->imd,
pDrive->track,
i8272_info->fdc_head,
i8272_info->fdc_sector,
sdata.raw,
128 << i8272_info->fdc_sec_len,
&flags,
&readlen);
for(i=0;i<(128 << i8272_info->fdc_sec_len);i++) {
PutByteDMA(i8272_info->fdc_dma_addr, sdata.raw[i]);
i8272_info->fdc_dma_addr++;
}
sim_debug(RD_DATA_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" T:%d/H:%d/S:%d/L:%4d: Data transferred to RAM at 0x%06x\n",
PCX, pDrive->track,
i8272_info->fdc_head,
i8272_info->fdc_sector,
128 << i8272_info->fdc_sec_len,
i8272_info->fdc_dma_addr - i);
} else { /* Write */
for(i=0;i<(128 << i8272_info->fdc_sec_len);i++) {
sdata.raw[i] = GetByteDMA(i8272_info->fdc_dma_addr);
i8272_info->fdc_dma_addr++;
}
sim_debug(WR_DATA_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Data transferred from RAM at 0x%06x\n",
PCX, i8272_info->fdc_dma_addr);
sectWrite(pDrive->imd,
pDrive->track,
i8272_info->fdc_head,
i8272_info->fdc_sector,
sdata.raw,
128 << i8272_info->fdc_sec_len,
&flags,
&readlen);
}
i8272_info->result[5] = i8272_info->fdc_sector;
i8272_info->result[1] = 0x80;
}
break;
case I8272_FORMAT_TRACK: /* FORMAT A TRACK */
for(i8272_info->fdc_sector = 1;i8272_info->fdc_sector<=i8272_info->fdc_sc;i8272_info->fdc_sector++) {
sim_debug(CMD_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Format Track %d, Sector=%d, len=%d\n", PCX, pDrive->track, i8272_info->fdc_sector, 128 << i8272_info->fdc_sec_len);
if(i8272_info->fdc_sectorcount >= I8272_MAX_SECTOR) {
sim_debug(ERROR_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Illegal sector count\n", PCX);
i8272_info->fdc_sectorcount = 0;
}
i8272_info->fdc_sectormap[i8272_info->fdc_sectorcount] = i8272_info->fdc_sector;
i8272_info->fdc_sectorcount++;
if(i8272_info->fdc_sectorcount == i8272_info->fdc_sc) {
trackWrite(pDrive->imd,
pDrive->track,
i8272_info->fdc_head,
i8272_info->fdc_sc,
128 << i8272_info->fdc_sec_len,
i8272_info->fdc_sectormap,
i8272_info->fdc_mfm ? 3 : 0,
i8272_info->fdc_fillbyte,
&flags);
/* Recalculate disk size */
pDrive->uptr->capac = sim_fsize(pDrive->uptr->fileref);
}
}
break;
case I8272_SCAN_LOW_EQUAL: /* SCAN LOW OR EQUAL */
case I8272_SCAN_HIGH_EQUAL: /* SCAN HIGH OR EQUAL */
case I8272_SCAN_EQUAL: /* SCAN EQUAL */
sim_debug(CMD_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" Scan Data\n", PCX);
sim_debug(ERROR_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" ERROR: Scan not implemented.\n", PCX);
break;
case I8272_READ_ID: /* READ ID */
sim_debug(CMD_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT
" READ ID Drive %d result ST0=%02x ST1=%02x ST2=%02x "
"C=%d H=%d R=%02x N=%d\n", PCX,
i8272_info->sel_drive,
i8272_info->result[0], i8272_info->result[1],
i8272_info->result[2], i8272_info->result[3],
i8272_info->result[4], i8272_info->result[5],
i8272_info->result[6]);
break;
default:
break;
}
}
if(i8272_info->result_len != 0) {
i8272_info->fdc_phase ++;
} else {
i8272_info->fdc_phase = CMD_PHASE;
}
i8272_info->result_index = 0;
if((i8272_info->cmd[0] & 0x1F) != I8272_SENSE_INTR_STATUS) {
raise_i8272_interrupt();
}
}
break;
}
cData = 0x00;
return (cData);
}
static uint8 I8272_Setup_Cmd(uint8 fdc_cmd)
{
uint8 result = 0;
switch(fdc_cmd) {
case I8272_READ_DATA:
case I8272_WRITE_DATA:
case I8272_READ_DELETED_DATA:
case I8272_WRITE_DELETED_DATA:
case I8272_READ_TRACK:
case I8272_SCAN_LOW_EQUAL:
case I8272_SCAN_HIGH_EQUAL:
case I8272_SCAN_EQUAL:
i8272_info->cmd_len = 9;
i8272_info->result_len = 7;
break;
case I8272_READ_ID: /* READ ID */
i8272_info->cmd_len = 2;
i8272_info->result_len = 7;
break;
case I8272_RECALIBRATE: /* RECALIBRATE */
i8272_info->cmd_len = 2;
i8272_info->result_len = 0;
break;
case I8272_FORMAT_TRACK: /* FORMAT A TRACK */
i8272_info->cmd_len = 6;
i8272_info->result_len = 7;
break;
case I8272_SENSE_INTR_STATUS: /* SENSE INTERRUPT STATUS */
i8272_info->cmd_len = 1;
i8272_info->result_len = 2;
break;
case I8272_SPECIFY: /* SPECIFY */
i8272_info->cmd_len = 3;
i8272_info->result_len = 0;
break;
case I8272_SENSE_DRIVE_STATUS: /* SENSE DRIVE STATUS */
i8272_info->cmd_len = 2;
i8272_info->result_len = 1;
break;
case I8272_SEEK: /* SEEK */
i8272_info->cmd_len = 3;
i8272_info->result_len = 0;
break;
default: /* INVALID */
i8272_info->cmd_len = 1;
i8272_info->result_len = 1;
result = -1;
break;
}
return (result);
}
extern void raise_disk1a_interrupt(void);
static void raise_i8272_interrupt(void)
{
sim_debug(IRQ_MSG, &i8272_dev, "I8272: " ADDRESS_FORMAT " FDC Interrupt\n", PCX);
i8272_irq = 1;
raise_disk1a_interrupt();
}