Because some key files have changed, V3.0 should be unzipped to a clean directory. 1. New Features in 3.0-0 1.1 SCP and Libraries - Added ASSIGN/DEASSIGN (logical name) commands. - Changed RESTORE to unconditionally detach files. - Added E11 and TPC format support to magtape library. - Fixed bug in SHOW CONNECTIONS. - Added USE_ADDR64 support 1.2 All magtapes - Magtapes support SIMH format, E11 format, and TPC format (read only). - SET <tape_unit> FORMAT=format sets the specified tape unit's format. - SHOW <tape_unit> FORMAT displays the specified tape unit's format. - Tape format can also be set as part of the ATTACH command, using the -F switch. 1.3 VAX - VAX can be compiled without USE_INT64. - If compiled with USE_INT64 and USE_ADDR64, RQ and TQ controllers support files > 2GB. - VAX ROM has speed control (SET ROM DELAY/NODELAY). 2. Bugs Fixed in 3.01-0 2.1 VAX - Fixed CVTfi bug: integer overflow not set if exponent out of range - Fixed EMODx bugs: o First and second operands reversed o Separated fraction received wrong exponent o Overflow calculation on separated integer incorrect o Fraction not set to zero if exponent out of range - Fixed interval timer and ROM access to pass power-up self-test even on very fast host processors (fixes from Mark Pizzolato). 2.2 1401 - Fixed mnemonic, instruction lengths, and reverse scan length check bug for MCS. - Fixed MCE bug, BS off by 1 if zero suppress. - Fixed chaining bug, D lost if return to SCP. - Fixed H branch, branch occurs after continue. - Added check for invalid 8 character MCW, LCA. - Fixed magtape load-mode end of record response. 2.3 Nova - Fixed DSK variable size interaction with restore. 2.4 PDP-1 - Fixed DT variable size interaction with restore. 2.5 PDP-11 - Fixed DT variable size interaction with restore. - Fixed bug in MMR1 update (found by Tim Stark). - Added XQ features and fixed bugs: o Corrected XQ interrupts on IE state transition (code by Tom Evans). o Added XQ interrupt clear on soft reset. o Removed XQ interrupt when setting XL or RL (multiple people). o Added SET/SHOW XQ STATS. o Added SHOW XQ FILTERS. o Added ability to split received packet into multiple buffers. o Added explicit runt and giant packet processing. 2.6 PDP-18B - Fixed DT, RF variable size interaction with restore. - Fixed MT bug in MTTR. 2.7 PDP-8 - Fixed DT, DF, RF, RX variable size interaction with restore. - Fixed MT bug in SKTR. 2.8 HP2100 - Fixed bug in DP (13210A controller only), DQ read status. - Fixed bug in DP, DQ seek complete. 2.9 GRI - Fixed bug in SC queue pointer management. 3. New Features in 3.0 vs prior releases N/A 4. Bugs Fixed in 3.0 vs prior releases N/A 5. General Notes WARNING: The RESTORE command has changed. RESTORE will now detach an attached file on a unit, if that unit did not have an attached file in the saved configuration. This is required to assure that the unit flags and the file state are consistent. WARNING: The compilation scheme for the PDP-10, PDP-11, and VAX has changed. Use one of the supplied build files, or read the documentation carefully, before compiling any of these simulators.
386 lines
14 KiB
C
386 lines
14 KiB
C
/* altairz80_hdsk.c: simulated hard disk device to increase capacity
|
|
|
|
Copyright (c) 2002-2003, Peter Schorn
|
|
|
|
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
|
|
PETER SCHORN 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 Peter Schorn shall not
|
|
be used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Peter Schorn.
|
|
*/
|
|
|
|
#include "altairz80_defs.h"
|
|
|
|
#define UNIT_V_HDSKWLK (UNIT_V_UF + 0) /* write locked */
|
|
#define UNIT_HDSKWLK (1 << UNIT_V_HDSKWLK)
|
|
#define UNIT_V_HDSK_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
|
|
#define UNIT_HDSK_VERBOSE (1 << UNIT_V_HDSK_VERBOSE)
|
|
#define HDSK_SECTOR_SIZE 128 /* size of sector */
|
|
#define HDSK_SECTORS_PER_TRACK 32 /* sectors per track */
|
|
#define HDS_MAX_TRACKS 2048 /* number of tracks */
|
|
#define HDSK_TRACK_SIZE (HDSK_SECTOR_SIZE * HDSK_SECTORS_PER_TRACK)
|
|
#define HDSK_CAPACITY (HDSK_TRACK_SIZE * HDS_MAX_TRACKS)
|
|
#define HDSK_NUMBER 8 /* number of hard disks */
|
|
#define CPM_OK 0 /* indicates to CP/M everything ok */
|
|
#define CPM_ERROR 1 /* indicates to CP/M an error condition */
|
|
#define CPM_EMPTY 0xe5 /* default value for non-existing bytes */
|
|
#define hdsk_none 0
|
|
#define hdsk_reset 1
|
|
#define hdsk_read 2
|
|
#define hdsk_write 3
|
|
#define hdsk_boot_address 0x5c00
|
|
|
|
extern char messageBuffer[];
|
|
extern int32 PCX;
|
|
extern UNIT cpu_unit;
|
|
extern uint8 M[MAXMEMSIZE][MAXBANKS];
|
|
extern int32 saved_PC;
|
|
|
|
extern int32 install_bootrom(void);
|
|
extern void printMessage(void);
|
|
extern void PutBYTEBasic(const uint32 Addr, const uint32 Bank, const uint32 Value);
|
|
extern void PutBYTEWrapper(register uint32 Addr, register uint32 Value);
|
|
extern void protect(const int32 l, const int32 h);
|
|
extern uint8 GetBYTEWrapper(register uint32 Addr);
|
|
extern int32 bootrom[bootrom_size];
|
|
|
|
static t_stat hdsk_svc(UNIT *uptr);
|
|
static t_stat hdsk_boot(int32 unitno, DEVICE *dptr);
|
|
static int32 hdsk_hasVerbose(void);
|
|
int32 hdsk_io(const int32 port, const int32 io, const int32 data);
|
|
static int32 hdsk_in(const int32 port);
|
|
static int32 hdsk_out(const int32 data);
|
|
static int32 checkParameters(void);
|
|
static int32 doSeek(void);
|
|
static int32 doRead(void);
|
|
static int32 doWrite(void);
|
|
|
|
static int32 hdskLastCommand = hdsk_none;
|
|
static int32 hdskCommandPosition = 0;
|
|
static int32 selectedDisk;
|
|
static int32 selectedSector;
|
|
static int32 selectedTrack;
|
|
static int32 selectedDMA;
|
|
static int32 hdskTrace;
|
|
|
|
static UNIT hdsk_unit[] = {
|
|
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
|
|
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
|
|
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
|
|
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
|
|
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
|
|
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
|
|
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
|
|
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) } };
|
|
|
|
static REG hdsk_reg[] = {
|
|
{ DRDATA (HDCMD, hdskLastCommand, 32), REG_RO },
|
|
{ DRDATA (HDPOS, hdskCommandPosition, 32), REG_RO },
|
|
{ DRDATA (HDDSK, selectedDisk, 32), REG_RO },
|
|
{ DRDATA (HDSEC, selectedSector, 32), REG_RO },
|
|
{ DRDATA (HDTRK, selectedTrack, 32), REG_RO },
|
|
{ DRDATA (HDDMA, selectedDMA, 32), REG_RO },
|
|
{ DRDATA (HDTRACE, hdskTrace, 8), },
|
|
{ NULL } };
|
|
|
|
static MTAB hdsk_mod[] = {
|
|
{ UNIT_HDSKWLK, 0, "write enabled", "WRITEENABLED", NULL },
|
|
{ UNIT_HDSKWLK, UNIT_HDSKWLK, "write locked", "LOCKED", NULL },
|
|
/* quiet, no warning messages */
|
|
{ UNIT_HDSK_VERBOSE, 0, "QUIET", "QUIET", NULL },
|
|
/* verbose, show warning messages */
|
|
{ UNIT_HDSK_VERBOSE, UNIT_HDSK_VERBOSE, "VERBOSE", "VERBOSE", NULL },
|
|
{ 0 } };
|
|
|
|
DEVICE hdsk_dev = {
|
|
"HDSK", hdsk_unit, hdsk_reg, hdsk_mod,
|
|
8, 10, 31, 1, 8, 8,
|
|
NULL, NULL, NULL,
|
|
&hdsk_boot, NULL, NULL, NULL, 0, NULL, NULL };
|
|
|
|
static t_stat hdsk_svc(UNIT *uptr) {
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static const int32 hdskBoot[bootrom_size] = {
|
|
0xf3, 0x06, 0x80, 0x3e, 0x0e, 0xd3, 0xfe, 0x05, /* 5c00-5c07 */
|
|
0xc2, 0x05, 0x5c, 0x3e, 0x16, 0xd3, 0xfe, 0x3e, /* 5c08-5c0f */
|
|
0x12, 0xd3, 0xfe, 0xdb, 0xfe, 0xb7, 0xca, 0x20, /* 5c10-5c17 */
|
|
0x5c, 0x3e, 0x0c, 0xd3, 0xfe, 0xaf, 0xd3, 0xfe, /* 5c18-5c1f */
|
|
0x06, 0x20, 0x3e, 0x01, 0xd3, 0xfd, 0x05, 0xc2, /* 5c20-5c27 */
|
|
0x24, 0x5c, 0x11, 0x08, 0x00, 0x21, 0x00, 0x00, /* 5c28-5c2f */
|
|
0x0e, 0xb8, 0x3e, 0x02, 0xd3, 0xfd, 0x3a, 0x37, /* 5c30-5c37 */
|
|
0xff, 0xd6, 0x08, 0xd3, 0xfd, 0x7b, 0xd3, 0xfd, /* 5c38-5c3f */
|
|
0x7a, 0xd3, 0xfd, 0xaf, 0xd3, 0xfd, 0x7d, 0xd3, /* 5c40-5c47 */
|
|
0xfd, 0x7c, 0xd3, 0xfd, 0xdb, 0xfd, 0xb7, 0xca, /* 5c48-5c4f */
|
|
0x53, 0x5c, 0x76, 0x79, 0x0e, 0x80, 0x09, 0x4f, /* 5c50-5c57 */
|
|
0x0d, 0xc2, 0x60, 0x5c, 0xfb, 0xc3, 0x00, 0x00, /* 5c58-5c5f */
|
|
0x1c, 0x1c, 0x7b, 0xfe, 0x20, 0xca, 0x73, 0x5c, /* 5c60-5c67 */
|
|
0xfe, 0x21, 0xc2, 0x32, 0x5c, 0x1e, 0x00, 0x14, /* 5c68-5c6f */
|
|
0xc3, 0x32, 0x5c, 0x1e, 0x01, 0xc3, 0x32, 0x5c, /* 5c70-5c77 */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c78-5c7f */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c80-5c87 */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c88-5c8f */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c90-5c97 */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c98-5c9f */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca0-5ca7 */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca8-5caf */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb0-5cb7 */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb8-5cbf */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc0-5cc7 */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc8-5ccf */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd0-5cd7 */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd8-5cdf */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce0-5ce7 */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce8-5cef */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf0-5cf7 */
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf8-5cff */
|
|
};
|
|
|
|
static t_stat hdsk_boot(int32 unitno, DEVICE *dptr) {
|
|
int32 i;
|
|
if (MEMSIZE < 24*KB) {
|
|
printf("Need at least 24KB RAM to boot from hard disk.\n");
|
|
return SCPE_ARG;
|
|
}
|
|
if (cpu_unit.flags & (UNIT_ALTAIRROM | UNIT_BANKED)) {
|
|
if (install_bootrom()) {
|
|
printf("ALTAIR boot ROM installed.\n");
|
|
}
|
|
/* check whether we are really modifying an LD A,<> instruction */
|
|
if (bootrom[unitNoOffset1 - 1] == LDAInstruction) {
|
|
bootrom[unitNoOffset1] = (unitno + NUM_OF_DSK) & 0xff; /* LD A,<unitno> */
|
|
}
|
|
else { /* Attempt to modify non LD A,<> instructions is refused. */
|
|
printf("Incorrect boot ROM offset detected.\n");
|
|
return SCPE_IERR;
|
|
}
|
|
}
|
|
for (i = 0; i < bootrom_size; i++) {
|
|
PutBYTEBasic(i + hdsk_boot_address, 0, hdskBoot[i] & 0xff);
|
|
}
|
|
saved_PC = hdsk_boot_address;
|
|
protect(hdsk_boot_address, hdsk_boot_address + bootrom_size - 1);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* returns TRUE iff there exists a disk with VERBOSE */
|
|
static int32 hdsk_hasVerbose(void) {
|
|
int32 i;
|
|
for (i = 0; i < HDSK_NUMBER; i++) {
|
|
if (((hdsk_dev.units + i) -> flags) & UNIT_HDSK_VERBOSE) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* The hard disk port is 0xfd. It understands the following commands.
|
|
|
|
1. reset
|
|
ld b,32
|
|
ld a,hdsk_reset
|
|
l out (0fdh),a
|
|
dec b
|
|
jp nz,l
|
|
|
|
2. read / write
|
|
; parameter block
|
|
cmd: db hdsk_read or hdsk_write
|
|
hd: db 0 ; 0 .. 7, defines hard disk to be used
|
|
sector: db 0 ; 0 .. 31, defines sector
|
|
track: dw 0 ; 0 .. 2047, defines track
|
|
dma: dw 0 ; defines where result is placed in memory
|
|
|
|
; routine to execute
|
|
ld b,7 ; size of parameter block
|
|
ld hl,cmd ; start address of parameter block
|
|
l ld a,(hl) ; get byte of parameter block
|
|
out (0fdh),a ; send it to port
|
|
inc hl ; point to next byte
|
|
dec b ; decrement counter
|
|
jp nz,l ; again, if not done
|
|
in a,(0fdh) ; get result code
|
|
|
|
*/
|
|
|
|
/* check the parameters and return TRUE iff parameters are correct or have been repaired */
|
|
static int32 checkParameters(void) {
|
|
int32 currentFlag;
|
|
if ((selectedDisk < 0) || (selectedDisk >= HDSK_NUMBER)) {
|
|
if (hdsk_hasVerbose()) {
|
|
message2("HDSK%d does not exist, will use HDSK0 instead.\n", selectedDisk);
|
|
}
|
|
selectedDisk = 0;
|
|
}
|
|
currentFlag = (hdsk_dev.units + selectedDisk) -> flags;
|
|
if ((currentFlag & UNIT_ATT) == 0) {
|
|
if (currentFlag & UNIT_HDSK_VERBOSE) {
|
|
message2("HDSK%d is not attached.\n", selectedDisk);
|
|
}
|
|
return FALSE; /* cannot read or write */
|
|
}
|
|
if ((selectedSector < 0) || (selectedSector >= HDSK_SECTORS_PER_TRACK)) {
|
|
if (currentFlag & UNIT_HDSK_VERBOSE) {
|
|
message4("HDSK%d: 0 <= Sector=%02d < %d violated, will use 0 instead.\n",
|
|
selectedDisk, selectedSector, HDSK_SECTORS_PER_TRACK);
|
|
}
|
|
selectedSector = 0;
|
|
}
|
|
if ((selectedTrack < 0) || (selectedTrack >= HDS_MAX_TRACKS)) {
|
|
if (currentFlag & UNIT_HDSK_VERBOSE) {
|
|
message4("HDSK%d: 0 <= Track=%04d < %04d violated, will use 0 instead.\n",
|
|
selectedDisk, selectedTrack, HDS_MAX_TRACKS);
|
|
}
|
|
selectedTrack = 0;
|
|
}
|
|
selectedDMA &= ADDRMASK;
|
|
if (hdskTrace) {
|
|
message6("%s HDSK%d Sector=%02d Track=%04d DMA=%04x\n",
|
|
(hdskLastCommand == hdsk_read) ? "Read" : "Write",
|
|
selectedDisk, selectedSector, selectedTrack, selectedDMA);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static int32 doSeek(void) {
|
|
UNIT *uptr = hdsk_dev.units + selectedDisk;
|
|
if (fseek(uptr -> fileref,
|
|
HDSK_TRACK_SIZE * selectedTrack + HDSK_SECTOR_SIZE * selectedSector, SEEK_SET)) {
|
|
if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {
|
|
message4("Could not access HDSK%d Sector=%02d Track=%04d.\n",
|
|
selectedDisk, selectedSector, selectedTrack);
|
|
}
|
|
return CPM_ERROR;
|
|
}
|
|
else {
|
|
return CPM_OK;
|
|
}
|
|
}
|
|
|
|
static int32 doRead(void) {
|
|
int32 i;
|
|
uint8 hdskbuf[HDSK_SECTOR_SIZE]; /* data buffer */
|
|
UNIT *uptr = hdsk_dev.units + selectedDisk;
|
|
if (doSeek()) {
|
|
return CPM_ERROR;
|
|
}
|
|
if (fread(hdskbuf, HDSK_SECTOR_SIZE, 1, uptr -> fileref) != 1) {
|
|
for (i = 0; i < HDSK_SECTOR_SIZE; i++) {
|
|
hdskbuf[i] = CPM_EMPTY;
|
|
}
|
|
if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {
|
|
message4("Could not read HDSK%d Sector=%02d Track=%04d.\n",
|
|
selectedDisk, selectedSector, selectedTrack);
|
|
}
|
|
return CPM_OK; /* allows the creation of empty hard disks */
|
|
}
|
|
for (i = 0; i < HDSK_SECTOR_SIZE; i++) {
|
|
PutBYTEWrapper(selectedDMA + i, hdskbuf[i]);
|
|
}
|
|
return CPM_OK;
|
|
}
|
|
|
|
static int32 doWrite(void) {
|
|
int32 i;
|
|
uint8 hdskbuf[HDSK_SECTOR_SIZE]; /* data buffer */
|
|
UNIT *uptr = hdsk_dev.units + selectedDisk;
|
|
if (((uptr -> flags) & UNIT_HDSKWLK) == 0) { /* write enabled */
|
|
if (doSeek()) {
|
|
return CPM_ERROR;
|
|
}
|
|
for (i = 0; i < HDSK_SECTOR_SIZE; i++) {
|
|
hdskbuf[i] = GetBYTEWrapper(selectedDMA + i);
|
|
}
|
|
if (fwrite(hdskbuf, HDSK_SECTOR_SIZE, 1, uptr -> fileref) != 1) {
|
|
if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {
|
|
message4("Could not write HDSK%d Sector=%02d Track=%04d.\n",
|
|
selectedDisk, selectedSector, selectedTrack);
|
|
}
|
|
return CPM_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {
|
|
message4("Could not write to locked HDSK%d Sector=%02d Track=%04d.\n",
|
|
selectedDisk, selectedSector, selectedTrack);
|
|
}
|
|
return CPM_ERROR;
|
|
}
|
|
return CPM_OK;
|
|
}
|
|
|
|
static int32 hdsk_in(const int32 port) {
|
|
int32 result;
|
|
if ((hdskCommandPosition == 6) && ((hdskLastCommand == hdsk_read) || (hdskLastCommand == hdsk_write))) {
|
|
result = checkParameters() ? ((hdskLastCommand == hdsk_read) ? doRead() : doWrite()) : CPM_ERROR;
|
|
hdskLastCommand = hdsk_none;
|
|
hdskCommandPosition = 0;
|
|
return result;
|
|
}
|
|
else if (hdsk_hasVerbose()) {
|
|
message4("Illegal IN command detected (port=%03xh, cmd=%d, pos=%d).\n",
|
|
port, hdskLastCommand, hdskCommandPosition);
|
|
}
|
|
return CPM_OK;
|
|
}
|
|
|
|
static int32 hdsk_out(const int32 data) {
|
|
switch(hdskLastCommand) {
|
|
case hdsk_read:
|
|
case hdsk_write:
|
|
switch(hdskCommandPosition) {
|
|
case 0:
|
|
selectedDisk = data;
|
|
hdskCommandPosition++;
|
|
break;
|
|
case 1:
|
|
selectedSector = data;
|
|
hdskCommandPosition++;
|
|
break;
|
|
case 2:
|
|
selectedTrack = data;
|
|
hdskCommandPosition++;
|
|
break;
|
|
case 3:
|
|
selectedTrack += (data << 8);
|
|
hdskCommandPosition++;
|
|
break;
|
|
case 4:
|
|
selectedDMA = data;
|
|
hdskCommandPosition++;
|
|
break;
|
|
case 5:
|
|
selectedDMA += (data << 8);
|
|
hdskCommandPosition++;
|
|
break;
|
|
default:
|
|
hdskLastCommand = hdsk_none;
|
|
hdskCommandPosition = 0;
|
|
}
|
|
break;
|
|
default:
|
|
hdskLastCommand = data;
|
|
hdskCommandPosition = 0;
|
|
}
|
|
return 0; /* ignored, since OUT */
|
|
}
|
|
|
|
int32 hdsk_io(const int32 port, const int32 io, const int32 data) {
|
|
return io == 0 ? hdsk_in(port) : hdsk_out(data);
|
|
}
|