1. New Features in 2.10-2 The build procedures have changed. There is only one UNIX makefile. To compile without Ethernet support, simply type gmake {target|all} To compile with Ethernet support, type gmake USE_NETWORK=1 {target|all} The Mingw batch files require Mingw release 2 and invoke the Unix makefile. There are still separate batch files for compilation with or without Ethernet support. 1.1 SCP and Libraries - The EVAL command will evaluate a symbolic type-in and display it in numeric form. - The ! command (with no arguments) will launch the host operating system command shell. The ! command (with an argument) executes the argument as a host operating system command. (Code from Mark Pizzolato) - Telnet sessions now recognize BREAK. How a BREAK is transmitted dependent on the particular Telnet client. (Code from Mark Pizzolato) - The sockets library includes code for active connections as well as listening connections. - The RESTORE command will restore saved memory size, if the simulator supports dynamic memory resizing. 1.2 PDP-1 - The PDP-1 supports the Type 24 serial drum (based on recently discovered documents). 1.3 18b PDP's - The PDP-4 supports the Type 24 serial drum (based on recently discovered documents). 1.4 PDP-11 - The PDP-11 implements a stub DEUNA/DELUA (XU). The real XU module will be included in a later release. 1.5 PDP-10 - The PDP-10 implements a stub DEUNA/DELUA (XU). The real XU module will be included in a later release. 1.6 HP 2100 - The IOP microinstruction set is supported for the 21MX as well as the 2100. - The HP2100 supports the Access Interprocessor Link (IPL). 1.7 VAX - If the VAX console is attached to a Telnet session, BREAK is interpreted as console halt. - The SET/SHOW HISTORY commands enable and display a history of the most recently executed instructions. (Code from Mark Pizzolato) 1.8 Terminals Multiplexors - BREAK detection was added to the HP, DEC, and Interdata terminal multiplexors. 1.9 Interdata 16b and 32b - First release. UNIX is not yet working. 1.10 SDS 940 - First release. 2. Bugs Fixed in 2.10-2 - PDP-11 console must default to 7b for early UNIX compatibility. - PDP-11/VAX TMSCP emulator was using the wrong packet length for read/write end packets. - Telnet IAC+IAC processing was fixed, both for input and output (found by Mark Pizzolato). - PDP-11/VAX Ethernet setting flag bits wrong for chained descriptors (found by Mark Pizzolato). 3. New Features in 2.10 vs prior releases 3.1 SCP and Libraries - The VT emulation package has been replaced by the capability to remote the console to a Telnet session. Telnet clients typically have more complete and robust VT100 emulation. - Simulated devices may now have statically allocated buffers, in addition to dynamically allocated buffers or disk-based data stores. - The DO command now takes substitutable arguments (max 9). In command files, %n represents substitutable argument n. - The initial command line is now interpreted as the command name and substitutable arguments for a DO command. This is backward compatible to prior versions. - The initial command line parses switches. -Q is interpreted as quiet mode; informational messages are suppressed. - The HELP command now takes an optional argument. HELP <cmd> types help on the specified command. - Hooks have been added for implementing GUI-based consoles, as well as simulator-specific command extensions. A few internal data structures and definitions have changed. - Two new routines (tmxr_open_master, tmxr_close_master) have been added to sim_tmxr.c. The calling sequence for sim_accept_conn has been changed in sim_sock.c. - The calling sequence for the VM boot routine has been modified to add an additional parameter. - SAVE now saves, and GET now restores, controller and unit flags. - Library sim_ether.c has been added for Ethernet support. 3.2 VAX - Non-volatile RAM (NVR) can behave either like a memory or like a disk-based peripheral. If unattached, it behaves like memory and is saved and restored by SAVE and RESTORE, respectively. If attached, its contents are loaded from disk by ATTACH and written back to disk at DETACH and EXIT. - SHOW <device> VECTOR displays the device's interrupt vector. A few devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The TK50 (TMSCP tape) has been added. - The DEQNA/DELQA (Qbus Ethernet controllers) have been added. - Autoconfiguration support has been added. - The paper tape reader has been removed from vax_stddev.c and now references a common implementation file, dec_pt.h. - Examine and deposit switches now work on all devices, not just the CPU. - Device address conflicts are not detected until simulation starts. 3.3 PDP-11 - SHOW <device> VECTOR displays the device's interrupt vector. Most devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The TK50 (TMSCP tape), RK611/RK06/RK07 (cartridge disk), RX211 (double density floppy), and KW11P programmable clock have been added. - The DEQNA/DELQA (Qbus Ethernet controllers) have been added. - Autoconfiguration support has been added. - The paper tape reader has been removed from pdp11_stddev.c and now references a common implementation file, dec_pt.h. - Device bootstraps now use the actual CSR specified by the SET ADDRESS command, rather than just the default CSR. Note that PDP-11 operating systems may NOT support booting with non-standard addresses. - Specifying more than 256KB of memory, or changing the bus configuration, causes all peripherals that are not compatible with the current bus configuration to be disabled. - Device address conflicts are not detected until simulation starts. 3.4 PDP-10 - SHOW <device> VECTOR displays the device's interrupt vector. A few devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The RX211 (double density floppy) has been added; it is off by default. - The paper tape now references a common implementation file, dec_pt.h. - Device address conflicts are not detected until simulation starts. 3.5 PDP-1 - DECtape (then known as MicroTape) support has been added. - The line printer and DECtape can be disabled and enabled. 3.6 PDP-8 - The RX28 (double density floppy) has been added as an option to the existing RX8E controller. - SHOW <device> DEVNO displays the device's device number. Most devices allow the device number to be changed with SET <device> DEVNO=nnn. - Device number conflicts are not detected until simulation starts. 3.7 IBM 1620 - The IBM 1620 simulator has been released. 3.8 AltairZ80 - A hard drive has been added for increased storage. - Several bugs have been fixed. 3.9 HP 2100 - The 12845A has been added and made the default line printer (LPT). The 12653A has been renamed LPS and is off by default. It also supports the diagnostic functions needed to run the DCPC and DMS diagnostics. - The 12557A/13210A disk defaults to the 13210A (7900/7901). - The 12559A magtape is off by default. - New CPU options (EAU/NOEAU) enable/disable the extended arithmetic instructions for the 2116. These instructions are standard on the 2100 and 21MX. - New CPU options (MPR/NOMPR) enable/disable memory protect for the 2100 and 21MX. - New CPU options (DMS/NODMS) enable/disable the dynamic mapping instructions for the 21MX. - The 12539 timebase generator autocalibrates. 3.10 Simulated Magtapes - Simulated magtapes recognize end of file and the marker 0xFFFFFFFF as end of medium. Only the TMSCP tape simulator can generate an end of medium marker. - The error handling in simulated magtapes was overhauled to be consistent through all simulators. 3.11 Simulated DECtapes - Added support for RT11 image file format (256 x 16b) to DECtapes. 4. Bugs Fixed in 2.10 vs prior releases - TS11/TSV05 was not simulating the XS0_MOT bit, causing failures under VMS. In addition, two of the CTL options were coded interchanged. - IBM 1401 tape was not setting a word mark under group mark for load mode reads. This caused the diagnostics to crash. - SCP bugs in ssh_break and set_logon were fixed (found by Dave Hittner). - Numerous bugs in the HP 2100 extended arithmetic, floating point, 21MX, DMS, and IOP instructions were fixed. Bugs were also fixed in the memory protect and DMS functions. The moving head disks (DP, DQ) were revised to simulate the hardware more accurately. Missing functions in DQ (address skip, read address) were added. - PDP-10 tape wouldn't boot, and then wouldn't read (reported by Michael Thompson and Harris Newman, respectively) - PDP-1 typewriter is half duplex, with only one shift state for both input and output (found by Derek Peschel) 5. General Notes WARNING: V2.10 has reorganized and renamed some of the definition files for the PDP-10, PDP-11, and VAX. Be sure to delete all previous source files before you unpack the Zip archive, or unpack it into a new directory structure. WARNING: V2.10 has a new, more comprehensive save file format. Restoring save files from previous releases will cause 'invalid register' errors and loss of CPU option flags, device enable/ disable flags, unit online/offline flags, and unit writelock flags. WARNING: If you are using Visual Studio .NET through the IDE, be sure to turn off the /Wp64 flag in the project settings, or dozens of spurious errors will be generated. WARNING: Compiling Ethernet support under Windows requires extra steps; see the Ethernet readme file. Ethernet support is currently available only for Windows, Linux, NetBSD, and OpenBSD.
480 lines
17 KiB
C
480 lines
17 KiB
C
/* id_fd.c: Interdata floppy disk simulator
|
||
|
||
Copyright (c) 2001-2002, Robert M Supnik
|
||
|
||
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
|
||
ROBERT M SUPNIK 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 Robert M Supnik shall not
|
||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||
in this Software without prior written authorization from Robert M Supnik.
|
||
|
||
fd M46-630 floppy disk
|
||
|
||
A diskette consists of 77 tracks, each with 26 sectors of 128B. The
|
||
Interdata floppy uses a logical record numbering scheme from 1 to 2002.
|
||
Physical tracks are numbered 0-76, physical sectors 1-26.
|
||
|
||
To allow for deleted data handling, a directory is appended to the end
|
||
of the image, one byte per LRN. Zero (the default) is a normal record,
|
||
non-zero a deleted record.
|
||
*/
|
||
|
||
#include "id_defs.h"
|
||
|
||
#define FD_NUMTR 77 /* tracks/disk */
|
||
#define FD_NUMSC 26 /* sectors/track */
|
||
#define FD_NUMBY 128 /* bytes/sector */
|
||
#define FD_NUMLRN (FD_NUMTR * FD_NUMSC) /* LRNs/disk */
|
||
#define FD_SIZE (FD_NUMLRN * FD_NUMBY) /* bytes/disk */
|
||
#define FD_NUMDR 4 /* drives/controller */
|
||
#define UNIT_V_WLK (UNIT_V_UF) /* write locked */
|
||
#define UNIT_WLK (1u << UNIT_V_UF)
|
||
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
|
||
#define LRN u3 /* last LRN */
|
||
#define FNC u4 /* last function */
|
||
#define GET_DA(x) (((x) - 1) * FD_NUMBY)
|
||
#define GET_TRK(x) (((x) - 1) / FD_NUMSC)
|
||
#define GET_SEC(x) ((((x) - 1) % FD_NUMSC) + 1)
|
||
#define LRN_BOOT 5 /* boot block LRN */
|
||
|
||
/* Command byte */
|
||
|
||
#define CMD_V_UNIT 4 /* unit */
|
||
#define CMD_M_UNIT 0x3
|
||
#define GET_UNIT(x) (((x) >> CMD_V_UNIT) & CMD_M_UNIT)
|
||
#define CMD_V_FNC 0 /* function */
|
||
#define CMD_M_FNC 0xF
|
||
#define GET_FNC(x) (((x) >> CMD_V_FNC) & CMD_M_FNC)
|
||
#define FNC_RD 0x1 /* read */
|
||
#define FNC_WR 0x2 /* write */
|
||
#define FNC_RDID 0x3 /* read ID */
|
||
#define FNC_RSTA 0x4 /* read status */
|
||
#define FNC_DEL 0x5 /* write deleted */
|
||
#define FNC_BOOT 0x6 /* boot */
|
||
#define FNC_STOP 0x7 /* stop */
|
||
#define FNC_RESET 0x8 /* reset */
|
||
#define FNC_FMT 0x9 /* format NI */
|
||
#define FNC_STOPPING 0x10 /* stopping */
|
||
|
||
/* Status byte, * = dynamic */
|
||
|
||
#define STA_WRP 0x80 /* *write prot */
|
||
#define STA_DEF 0x40 /* def track NI */
|
||
#define STA_DEL 0x20 /* del record */
|
||
#define STA_ERR 0x10 /* error */
|
||
#define STA_IDL 0x02 /* idle */
|
||
#define STA_OFL 0x01 /* fault */
|
||
#define STA_MASK (STA_DEF|STA_DEL|STA_ERR|STA_BSY|STA_IDL)
|
||
#define SET_EX (STA_ERR) /* set EX */
|
||
|
||
/* Extended status, 6 bytes, * = dynamic */
|
||
|
||
#define ES_SIZE 6
|
||
#define ES0_HCRC 0x80 /* ID CRC NI */
|
||
#define ES0_DCRC 0x40 /* data CRC NI */
|
||
#define ES0_LRN 0x20 /* illegal LRN */
|
||
#define ES0_WRP 0x10 /* *write prot */
|
||
#define ES0_ERR 0x08 /* error */
|
||
#define ES0_DEF 0x04 /* def trk NI */
|
||
#define ES0_DEL 0x02 /* del rec NI */
|
||
#define ES0_FLT 0x01 /* fault */
|
||
#define ES1_TK0 0x80 /* track 0 */
|
||
#define ES1_NRDY 0x40 /* not ready */
|
||
#define ES1_NOAM 0x20 /* no addr mk NI */
|
||
#define ES1_CMD 0x10 /* illegal cmd */
|
||
#define ES1_SKE 0x08 /* seek err NI */
|
||
#define ES1_UNS 0x04 /* unsafe NI */
|
||
#define ES1_UNIT 0x03 /* unit # */
|
||
|
||
/* Processing options for commands */
|
||
|
||
#define C_RD 0x1 /* cmd reads disk */
|
||
#define C_WD 0x2 /* cmd writes disk */
|
||
|
||
extern uint32 int_req[INTSZ], int_enb[INTSZ];
|
||
|
||
uint32 fd_sta = 0; /* status */
|
||
uint32 fd_cmd = 0; /* command */
|
||
uint32 fd_db = 0; /* data buffer */
|
||
uint32 fd_bptr = 0; /* buffer pointer */
|
||
uint8 fdxb[FD_NUMBY] = { 0 }; /* sector buffer */
|
||
uint8 fd_es[FD_NUMDR][ES_SIZE] = { 0 }; /* ext status */
|
||
uint32 fd_lrn = 0; /* log rec # */
|
||
uint32 fd_wdv = 0; /* wd valid */
|
||
uint32 fd_stopioe = 1; /* stop on error */
|
||
uint32 fd_arm = 0; /* intr arm */
|
||
int32 fd_ctime = 100; /* command time */
|
||
int32 fd_stime = 10; /* seek, per LRN */
|
||
int32 fd_xtime = 1; /* tr set time */
|
||
|
||
static uint32 ctab[16] = {
|
||
0, C_RD, C_WD, 0, /* 0, rd, wr, 0 */
|
||
0, C_WD, C_RD, 0, /* 0, del, boot, 0 */
|
||
0, 0, 0, 0,
|
||
0, 0, 0, 0 };
|
||
|
||
DEVICE fd_dev;
|
||
uint32 fd (uint32 dev, uint32 op, uint32 dat);
|
||
t_stat fd_svc (UNIT *uptr);
|
||
t_stat fd_reset (DEVICE *dptr);
|
||
t_stat fd_clr (DEVICE *dptr);
|
||
t_stat fd_boot (int32 unitno, DEVICE *dptr);
|
||
t_bool fd_dte (UNIT *uptr, t_bool wr);
|
||
uint32 fd_crc (uint32 crc, uint32 dat, uint32 cnt);
|
||
void fd_done (uint32 u, uint32 nsta, uint32 nes0, uint32 nes1);
|
||
void sched_seek (UNIT *uptr, int32 newlrn);
|
||
|
||
/* FD data structures
|
||
|
||
fd_dev FD device descriptor
|
||
fd_unit FD unit list
|
||
fd_reg FD register list
|
||
fd_mod FD modifier list
|
||
*/
|
||
|
||
DIB fd_dib = { d_FD, -1, v_FD, NULL, &fd, NULL };
|
||
|
||
UNIT fd_unit[] = {
|
||
{ UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
|
||
UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) },
|
||
{ UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
|
||
UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) },
|
||
{ UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
|
||
UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) },
|
||
{ UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
|
||
UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) } };
|
||
|
||
REG fd_reg[] = {
|
||
{ HRDATA (CMD, fd_cmd, 8) },
|
||
{ HRDATA (STA, fd_sta, 8) },
|
||
{ HRDATA (BUF, fd_db, 8) },
|
||
{ HRDATA (LRN, fd_lrn, 16) },
|
||
{ BRDATA (ESTA, fd_es, 16, 8, ES_SIZE * FD_NUMDR) },
|
||
{ BRDATA (DBUF, fdxb, 16, 8, FD_NUMBY) },
|
||
{ HRDATA (DBPTR, fd_bptr, 8) },
|
||
{ FLDATA (WDV, fd_wdv, 0) },
|
||
{ FLDATA (IREQ, int_req[l_FD], i_FD) },
|
||
{ FLDATA (IENB, int_enb[l_FD], i_FD) },
|
||
{ FLDATA (IARM, fd_arm, 0) },
|
||
{ DRDATA (CTIME, fd_ctime, 24), PV_LEFT },
|
||
{ DRDATA (STIME, fd_stime, 24), PV_LEFT },
|
||
{ DRDATA (XTIME, fd_xtime, 24), PV_LEFT },
|
||
{ FLDATA (STOP_IOE, fd_stopioe, 0) },
|
||
{ URDATA (ULRN, fd_unit[0].LRN, 16, 16, 0, FD_NUMDR, REG_HRO) },
|
||
{ URDATA (UFNC, fd_unit[0].FNC, 16, 8, 0, FD_NUMDR, REG_HRO) },
|
||
{ HRDATA (DEVNO, fd_dib.dno, 8), REG_HRO },
|
||
{ NULL } };
|
||
|
||
MTAB fd_mod[] = {
|
||
{ UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
|
||
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
|
||
{ MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
|
||
&set_dev, &show_dev, NULL },
|
||
{ 0 } };
|
||
|
||
DEVICE fd_dev = {
|
||
"FD", fd_unit, fd_reg, fd_mod,
|
||
FD_NUMDR, 16, 20, 1, 16, 8,
|
||
NULL, NULL, &fd_reset,
|
||
&fd_boot, NULL, NULL,
|
||
&fd_dib, DEV_DISABLE };
|
||
|
||
/* Floppy disk: IO routine */
|
||
|
||
uint32 fd (uint32 dev, uint32 op, uint32 dat)
|
||
{
|
||
int32 u, t, fnc;
|
||
UNIT *uptr;
|
||
|
||
fnc = GET_FNC (fd_cmd); /* get fnc */
|
||
u = GET_UNIT (fd_cmd); /* get unit */
|
||
uptr = fd_dev.units + u;
|
||
switch (op) { /* case IO op */
|
||
case IO_ADR: /* select */
|
||
return BY; /* byte only */
|
||
case IO_RD: /* read */
|
||
if (fd_sta & (STA_IDL | STA_BSY)) return fd_db; /* idle, busy? */
|
||
if (fd_bptr < FD_NUMBY) fd_db = fdxb[fd_bptr++];/* get byte */
|
||
if (fd_bptr >= FD_NUMBY) { /* buf end? */
|
||
if (ctab[fnc] & C_RD) { /* disk read? */
|
||
sched_seek (uptr, uptr->LRN + 1); /* sched read */
|
||
fd_sta = fd_sta | STA_BSY; } /* set busy */
|
||
else fd_bptr = 0; } /* just wrap */
|
||
if ((ctab[fnc] & C_RD) && fd_arm) /* if rd & arm, */
|
||
SET_INT (v_FD); /* interrupt */
|
||
return fd_db; /* return buf */
|
||
case IO_WD: /* write */
|
||
if (fd_sta & STA_IDL) { /* idle? */
|
||
fd_lrn = ((fd_lrn << 8) | dat) & DMASK16; /* insert byte */
|
||
fd_wdv = 1;
|
||
break; }
|
||
if (fd_bptr < FD_NUMBY) /* if room, */
|
||
fdxb[fd_bptr++] = fd_db = dat; /* store byte */
|
||
if (fd_bptr >= FD_NUMBY) { /* buf end? */
|
||
if (ctab[fnc] & C_WD) { /* disk write? */
|
||
sched_seek (uptr, uptr->LRN + 1); /* sched write */
|
||
fd_sta = fd_sta | STA_BSY; } /* set busy */
|
||
else fd_bptr = 0; } /* just wrap */
|
||
if ((ctab[fnc] & C_WD) && fd_arm) /* if wr & arm, */
|
||
SET_INT (v_FD); /* interrupt */
|
||
break;
|
||
case IO_SS: /* status */
|
||
t = fd_sta & STA_MASK; /* get status */
|
||
if ((uptr->flags & UNIT_ATT) == 0) t = t | STA_DU;
|
||
if (t & SET_EX) t = t | STA_EX; /* test for ex */
|
||
return t;
|
||
case IO_OC: /* command */
|
||
fd_arm = int_chg (v_FD, dat, fd_arm); /* upd int ctrl */
|
||
fnc = GET_FNC (dat); /* new fnc */
|
||
fd_cmd = dat; /* save cmd */
|
||
u = GET_UNIT (dat); /* get unit */
|
||
uptr = fd_dev.units + u;
|
||
if (fnc == FNC_STOP) { /* stop? */
|
||
uptr->FNC = uptr->FNC | FNC_STOPPING; /* flag stop */
|
||
if (sim_is_active (uptr)) break; /* busy? cont */
|
||
if (ctab[GET_FNC (uptr->FNC)] & C_WD) { /* write? */
|
||
sched_seek (uptr, uptr->LRN + 1); /* sched write */
|
||
fd_sta = fd_sta | STA_BSY; } /* set busy */
|
||
else fd_done (u, 0, 0, 0); /* nrml done */
|
||
break; }
|
||
else if (fd_sta & STA_IDL) { /* must be idle */
|
||
if (fnc != FNC_RSTA) { /* !rd status */
|
||
fd_sta = STA_BSY; /* busy, !idle */
|
||
fd_es[u][0] = 0;
|
||
fd_es[u][1] = u; } /* init ext sta */
|
||
else fd_sta = (fd_sta & ~STA_IDL) | STA_BSY;
|
||
if (fnc == FNC_BOOT) t = LRN_BOOT; /* boot? fixed sec */
|
||
else if (fd_wdv) t = fd_lrn; /* valid data? use */
|
||
else t = uptr->LRN; /* use prev */
|
||
fd_wdv = 0; /* data invalid */
|
||
fd_bptr = 0; /* init buffer */
|
||
uptr->FNC = fnc; /* save function */
|
||
uptr->LRN = t; /* save LRN */
|
||
if (ctab[fnc] & C_RD) sched_seek (uptr, t); /* seek now? */
|
||
else sim_activate (uptr, fd_ctime); } /* start cmd */
|
||
break; }
|
||
return 0;
|
||
}
|
||
|
||
/* Unit service; the action to be taken depends on command */
|
||
|
||
t_stat fd_svc (UNIT *uptr)
|
||
{
|
||
uint32 i, u, tk, sc, crc, fnc;
|
||
t_addr da;
|
||
|
||
u = uptr - fd_dev.units; /* get unit number */
|
||
fnc = GET_FNC (uptr->FNC); /* get function */
|
||
switch (fnc) { /* case on function */
|
||
|
||
case FNC_RESET: /* reset */
|
||
fd_clr (&fd_dev); /* clear device */
|
||
fd_done (u, 0, 0, 0); /* set idle */
|
||
return SCPE_OK;
|
||
|
||
case FNC_STOP: /* stop */
|
||
fd_done (u, 0, 0, 0); /* set idle */
|
||
return SCPE_OK;
|
||
|
||
case FNC_BOOT: /* boot, buf empty */
|
||
case FNC_RD: /* read, buf empty */
|
||
if (uptr->FNC & FNC_STOPPING) break; /* stopped? */
|
||
if (fd_dte (uptr, FALSE)) return SCPE_OK; /* xfr error? */
|
||
da = GET_DA (uptr->LRN); /* get disk addr */
|
||
for (i = 0; i < FD_NUMBY; i++) /* read sector */
|
||
fdxb[i] = *(((uint8 *) uptr->filebuf) + da + i);
|
||
if (*(((uint8 *) uptr->filebuf) + FD_SIZE + uptr->LRN - 1)) {
|
||
fd_sta = fd_sta | STA_DEL; /* deleted? set err */
|
||
fd_es[u][0] = fd_es[u][0] | ES0_DEL; }
|
||
fd_es[u][2] = GET_SEC (uptr->LRN); /* set ext sec/trk */
|
||
fd_es[u][3] = GET_TRK (uptr->LRN);
|
||
fd_bptr = 0; /* init buf */
|
||
uptr->LRN = uptr->LRN + 1; /* next block */
|
||
break;
|
||
|
||
case FNC_WR: case FNC_DEL: /* write block */
|
||
if (fd_dte (uptr, TRUE)) return SCPE_OK; /* xfr error? */
|
||
if (fd_bptr) { /* any transfer? */
|
||
da = GET_DA (uptr->LRN); /* get disk addr */
|
||
for (i = fd_bptr; i < FD_NUMBY; i++) /* pad sector */
|
||
fdxb[i] = fd_db;
|
||
for (i = 0; i < FD_NUMBY; i++) /* write sector */
|
||
*(((uint8 *) uptr->filebuf) + da + i) = fdxb[i];
|
||
*(((uint8 *) uptr->filebuf) + FD_SIZE + uptr->LRN - 1) =
|
||
(fnc == FNC_DEL)? 1: 0; /* write dir */
|
||
uptr->hwmark = uptr->capac; /* rewrite all */
|
||
fd_es[u][2] = GET_SEC (uptr->LRN); /* set ext sec/trk */
|
||
fd_es[u][3] = GET_TRK (uptr->LRN);
|
||
fd_bptr = 0; /* init buf */
|
||
uptr->LRN = uptr->LRN + 1; } /* next block */
|
||
break;
|
||
|
||
case FNC_RSTA: /* read status */
|
||
if (uptr->flags & UNIT_WPRT) /* wr protected? */
|
||
fd_es[u][0] = fd_es[u][0] | ES0_WRP;
|
||
if (GET_TRK (uptr->LRN) == 0) /* on track 0? */
|
||
fd_es[u][1] = fd_es[u][1] | ES1_TK0;
|
||
if ((uptr->flags & UNIT_BUF) == 0) { /* not attached? */
|
||
fd_es[u][0] = fd_es[u][0] | ES0_FLT; /* set err */
|
||
fd_es[u][1] = fd_es[u][1] | ES1_NRDY; }
|
||
for (i = 0; i < ES_SIZE; i++) fdxb[i] = fd_es[u][i]; /* copy to buf */
|
||
for (i = ES_SIZE; i < FD_NUMBY; i++) fdxb[i] = 0;
|
||
break;
|
||
|
||
case FNC_RDID: /* read ID */
|
||
if ((uptr->flags & UNIT_BUF) == 0) { /* not attached? */
|
||
fd_done (u, STA_ERR, ES0_ERR | ES0_FLT, ES1_NRDY);
|
||
return SCPE_OK; }
|
||
for (i = 0; i < FD_NUMBY; i++) fdxb[i] = 0; /* clr buf */
|
||
tk = GET_TRK (uptr->LRN); /* get track */
|
||
sc = GET_SEC (uptr->LRN); /* get sector */
|
||
fdxb[0] = tk & 0xFF; /* store track */
|
||
fdxb[2] = sc & 0xFF; /* store sector */
|
||
crc = fd_crc (0xFFFF, 0xFE00, 8); /* CRC addr mark */
|
||
crc = fd_crc (crc, tk << 8, 16); /* CRC track */
|
||
crc = fd_crc (crc, sc << 8, 16); /* CRC sector */
|
||
fdxb[4] = (crc >> 8) & 0xFF; /* store CRC */
|
||
fdxb[5] = crc & 0xFF;
|
||
break;
|
||
|
||
case FNC_FMT: /* format */
|
||
default:
|
||
fd_done (u, STA_ERR, ES0_ERR, ES1_CMD); /* ill cmd */
|
||
uptr->LRN = 1; /* on track 0 */
|
||
return SCPE_OK; }
|
||
|
||
if (uptr->FNC & FNC_STOPPING) { /* stopping? */
|
||
uptr->FNC = FNC_STOP; /* fnc = STOP */
|
||
sim_activate (uptr, fd_ctime); } /* schedule */
|
||
fd_sta = fd_sta & ~STA_BSY; /* clear busy */
|
||
if (fd_arm) SET_INT (v_FD); /* if armed, int */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Schedule seek */
|
||
|
||
void sched_seek (UNIT *uptr, int32 newlrn)
|
||
{
|
||
int32 diff = newlrn - uptr->LRN; /* LRN diff */
|
||
|
||
if (diff < 0) diff = -diff; /* ABS */
|
||
if (diff < 10) diff = 10; /* MIN 10 */
|
||
sim_activate (uptr, diff * fd_stime); /* schedule */
|
||
return;
|
||
}
|
||
|
||
/* Command complete */
|
||
|
||
void fd_done (uint32 u, uint32 nsta, uint32 nes0, uint32 nes1)
|
||
{
|
||
fd_sta = (fd_sta | STA_IDL | nsta) & ~STA_BSY; /* set idle */
|
||
if (fd_arm) SET_INT (v_FD); /* if armed, int */
|
||
fd_es[u][0] = fd_es[u][0] | nes0; /* set ext state */
|
||
fd_es[u][1] = fd_es[u][1] | nes1;
|
||
return;
|
||
}
|
||
|
||
/* Test for data transfer error */
|
||
|
||
t_bool fd_dte (UNIT *uptr, t_bool wr)
|
||
{
|
||
uint32 u = uptr - fd_dev.units;
|
||
|
||
if ((uptr->flags & UNIT_BUF) == 0) { /* not attached? */
|
||
fd_done (u, STA_ERR, ES0_ERR | ES0_FLT, ES1_NRDY);
|
||
return TRUE; }
|
||
if (wr && (uptr->flags & UNIT_WPRT)) { /* wr protected? */
|
||
fd_done (u, STA_ERR, ES0_ERR | ES0_WRP, 0);
|
||
return TRUE; }
|
||
if ((uptr->LRN == 0) || (uptr->LRN > FD_NUMLRN)) { /* bad LRN? */
|
||
fd_done (u, STA_ERR, ES0_ERR | ES0_LRN, 0);
|
||
return TRUE; }
|
||
return FALSE;
|
||
}
|
||
|
||
/* Header CRC calculation */
|
||
|
||
uint32 fd_crc (uint32 crc, uint32 dat, uint32 cnt)
|
||
{
|
||
uint32 i, wrk;
|
||
|
||
for (i = 0; i < cnt; i++) {
|
||
wrk = crc ^ dat;
|
||
crc = (crc << 1) & DMASK16;
|
||
if (wrk & SIGN16) crc = ((crc ^ 0x1020) + 1) & DMASK16;
|
||
dat = (dat << 1) & DMASK16; }
|
||
return crc;
|
||
}
|
||
|
||
/* Reset routine */
|
||
|
||
t_stat fd_clr (DEVICE *dptr)
|
||
{
|
||
int32 i, j;
|
||
UNIT *uptr;
|
||
|
||
fd_sta = STA_IDL; /* idle */
|
||
fd_cmd = 0; /* clear state */
|
||
fd_db = 0;
|
||
fd_bptr = 0;
|
||
fd_lrn = 1;
|
||
fd_wdv = 0;
|
||
for (i = 0; i < FD_NUMBY; i++) fdxb[i] = 0; /* clr xfr buf */
|
||
for (i = 0; i < FD_NUMDR; i++) { /* loop thru units */
|
||
for (j = 0; j < ES_SIZE; j++) fd_es[i][j] = 0; /* clr ext sta */
|
||
fd_es[i][2] = 1; /* sector 1 */
|
||
uptr = fd_dev.units + i;
|
||
sim_cancel (uptr); /* stop drive */
|
||
uptr->LRN = 1; /* clear state */
|
||
uptr->FNC = 0; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat fd_reset (DEVICE *dptr)
|
||
{
|
||
CLR_INT (v_FD); /* clear int */
|
||
CLR_ENB (v_FD); /* disable int */
|
||
fd_arm = 0; /* disarm int */
|
||
return fd_clr (dptr);;
|
||
}
|
||
|
||
/* Bootstrap routine */
|
||
|
||
#define BOOT_START 0x50
|
||
#define BOOT_LEN (sizeof (boot_rom) / sizeof (uint8))
|
||
|
||
static uint8 boot_rom[] = {
|
||
0xD5, 0x00, /* ST: AL CF */
|
||
0x00, 0xCF,
|
||
0x43, 0x00, /* BR 80 */
|
||
0x00, 0x80
|
||
};
|
||
|
||
t_stat fd_boot (int32 unitno, DEVICE *dptr)
|
||
{
|
||
extern uint32 PC, dec_flgs;
|
||
extern uint16 decrom[];
|
||
|
||
if (decrom[0xD5] & dec_flgs) return SCPE_NOFNC; /* AL defined? */
|
||
IOWriteBlk (BOOT_START, BOOT_LEN, boot_rom); /* copy boot */
|
||
IOWriteB (AL_DEV, fd_dib.dno); /* set dev no */
|
||
IOWriteB (AL_IOC, 0x86 + (unitno << CMD_V_UNIT)); /* set dev cmd, unit num */
|
||
IOWriteB (AL_SCH, 0); /* clr sch dev no */
|
||
PC = BOOT_START;
|
||
return SCPE_OK;
|
||
}
|