diff --git a/0readmeAsynchIO.txt b/0readmeAsynchIO.txt new file mode 100644 index 00000000..b41cf4f9 --- /dev/null +++ b/0readmeAsynchIO.txt @@ -0,0 +1,161 @@ +SIM_ASYNCH_IO + +Theory of operation. + +Features. + - Optional Use. Build with or without SIM_ASYNCH_IO defined and + simulators will still build and perform correctly when run. + - Consistent Save/Restore state. The state of a simulator saved + on a simulator with (or without) Asynch support can be restored + on any simulator of the same version with or without Asynch + support. + - Optimal behavior/performance with simulator running with or + without CPU idling enabled. + - Consistent minimum instruction scheduling delays when operating + with or without SIM_ASYNCH_IO. When SIM_ASYNCH_IO is emabled, + any operation which would have been scheduled to occurr in 'n' + instructions will still occur (from the simulated computer's + point of view) at least 'n' instructions after it was initiated. + +Benefits. + Allows a simulator to execute simulated instructions concurrently + with I/O operations which may take numerous milliseconds to perform. + Allows a simulated device to potentially avoid polling for the arrival + of data. Polling consumes host processor CPU cycles which may better + be spent executing simulated instructions or letting other host + processes run. Measurements made of available instruction execution + easily demonstrate the benefits of parallel instruction and I/O + activities. A VAX simulator with a process running a disk intensive + application in one process was able to process 11 X the number of + Dhrystone operations with Asynch I/O enabled. + +Asynch I/O is provided through a callback model. +SimH Libraries which provide Asynch I/O support: + sim_disk + sim_tape + sim_ether + +Requirements to use: +The Simulator's instruction loop needs to be modified to include a single +line which checks for asynchronouzly arrived events. The vax_cpu.c +module added the following line indicated by >>>: + + /* Main instruction loop */ + + for ( ;; ) { + + [...] +>>> AIO_CHECK_EVENT; + if (sim_interval <= 0) { /* chk clock queue */ + temp = sim_process_event (); + if (temp) + ABORT (temp); + SET_IRQL; /* update interrupts */ + } + +A global variable (sim_asynch_latency) is used to indicate the "interrupt +dispatch latency". This variable is the number of nanoseconds between checks +for completed asynchronous I/O. The default value is 4000 (4 usec) which +corresponds reasonably with simulated hardware. This variable controls +the computation of sim_asynch_inst_latency which is the number of simulated +instructions in the sim_asynch_latency interval. We are trying to avoid +checking for completed asynchronous I/O after every instruction since the +actual checking every instruction can slow down execution. Periodic checks +provide a balance which allows response similar to real hardware while also +providing minimal impact on actual instruction execution. Meanwhile, if +maximal response is desired, then the value of sim_asynch_latency can be +set sufficiently low to assure that sim_asynch_inst_latency computes to 1. +The sim_asynch_inst_latency is dynamically updated once per second in the +sim_rtcn_calb routine where clock to instruction execution is dynamically +determined. A simulator would usually add register definitions +to enable viewing and setting of these variables via scp: + +#if defined (SIM_ASYNCH_IO) + { DRDATA (LATENCY, sim_asynch_latency, 32), PV_LEFT }, + { DRDATA (INST_LATENCY, sim_asynch_inst_latency, 32), PV_LEFT }, +#endif + + +Naming conventions: +All of the routines implemented in sim_disk and sim_tape have been kept +in place. All routines which perform I/O have a variant routine available +with a "_a" appended to the the routine name with the addition of a single +parameter which indicates the asynch completion callback routine. For +example there now exists the routines: + t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max); + t_stat sim_tape_rdrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback); + +The Purpose of the callback function is to record the I/O completion status +and then to schedule the activation of the unit. + +Considerations: +Avoiding multiple concurrent users of the unit structure. While asynch +I/O is pending on a Unit, the unit should not otherwise be on the event +queue. The I/O completion will cause the Unit to be scheduled to run +immediately to actually dispatch control flow to the callback routine. +The callback routine is always called in the same thread which is +executing instructions. Since all simulator device data structures are +only referenced from this thread there are no host multi-processor cache +coherency issues to be concerned about. + +Arguments to the callback routine: +UNIT *, and IO Status +Requirements of the Callback routine. +The callback routine must save the I/O completion status in a place +which the next invocation of the unit service routine will reference +and act on it. This allows device code to return error conditions +back to scp in a consistent way without regard to how the callback +routine (and the actual I/O) may have been executed. + +Required change in device coding. +Devices which wish to leverage the benefits of asynch I/O must rearrange +the code which implements the unit service routine. This rearrangement +usually entails breaking the activities into two phases. The first phase +(I'll call the top half) involves performing whatever is needed to +initiate a call to perform an I/O operation with a callback argument. +Control is then immediately returned to the scp event dispatcher. +The callback routine needs to be coded to stash away the io completion +status and some indicator that an I/O has completed. +The top/bottom half separation of the unit service routine would be +coded to examine the I/O completion indicator and invoke the bottom half +code upon completion. The bottom half code should clear the I/O +completion indicator and then perform any activities which normally +need to occur after the I/O completes. Care should be taken while +performing these top/bottom half activities to return to the scp event +dispatcher with either SCPE_OK or an appropriate error code when needed. +The need to return error indications to the scp event dispatcher is why +the bottom half activities can't simply be performed in the +callback routine (the callback routine does not return a status). +Care should also be taken to realize that local variables in the +unit service routine will not directly survive between the separate +top and bottom half calls to the unit service routine. If any such +information must be referenced in both the top and bottom half code paths +then it must either be recomputed prior to the top/bottom half check +or not stored in local variables of the unit service routine. + +Run time requirements to use SIM_ASYNCH_IO. +The Posix threads API (pthreads) is required for asynchronous execution. +Most *nix platforms have these APIs available and on these platforms +simh is typically built with these available since on these platforms, +pthreads is required for simh networking support. Windows can also +utilize the pthreads APIs if the compile and run time support for the +win32Pthreads package has been installed on the build system and the +run time dll is available in the execution environment. + +Sample Asynch I/O device implementations. +The pdp11_rq.c module has been refactored to leverage the asynch I/O +features of the sim_disk library. The impact to this code to adopt the +asynch I/O paradigm was quite minimal. +The pdp11_rp.c module has also been refactored to leverage the asynch I/O +features of the sim_disk library. +The pdp11_tq.c module has been refactored to leverage the asynch I/O +features of the sim_tape library. The impact to this code to adopt the +asynch I/O paradigm was very significant. This was due to the two facts: +1) there are many different operations which can be requested of tape +devices and 2) some of the tmscp operations required many separate +operations on the physical device layer to perform a single tmscp request. +This issue was addressed by adding additional routines to the physical +device layer (in sim_tape.c) which combined these multiple operations. +This approach will dovetail well with a potential future addition of +operations on physical tapes as yet another supported tape format. + diff --git a/PDP11/pdp11_mscp.h b/PDP11/pdp11_mscp.h index 4e3c9698..9dd07c90 100644 --- a/PDP11/pdp11_mscp.h +++ b/PDP11/pdp11_mscp.h @@ -84,6 +84,7 @@ #define MD_ACL 0x0002 /* t avl: all class NI */ #define MD_NXU 0x0001 /* b gus: next unit */ #define MD_RIP 0x0001 /* d onl: allow rip NI */ +#define MD_SPD 0x0001 /* d avl: spin-down */ /* End flags */ diff --git a/PDP11/pdp11_rp.c b/PDP11/pdp11_rp.c index 1ae0bbe7..f3503cc7 100644 --- a/PDP11/pdp11_rp.c +++ b/PDP11/pdp11_rp.c @@ -25,6 +25,10 @@ rp RH/RP/RM moving head disks + 06-Mar-11 MP Converted to using sim_disk library and refactored + for Asynch I/O. + Set STIME value to default of 26 which allows VMS V4.x + to boot. 17-May-07 RMS CS1 DVA resides in device, not MBA 21-Nov-05 RMS Enable/disable device also enables/disables Massbus adapter 12-Nov-05 RMS Fixed DriveClear, does not clear disk address @@ -65,6 +69,7 @@ #endif +#include "sim_disk.h" #include #define RP_CTRL 0 /* ctrl is RP */ @@ -93,6 +98,9 @@ /* Parameters in the unit descriptor */ #define CYL u3 /* current cylinder */ +#define sectsread u4 /* sectors read */ +#define io_status u5 /* io status from callback */ +#define io_complete u6 /* io completion flag */ /* RPCS1, RMCS1 - control/status 1 - offset 0 */ @@ -319,19 +327,20 @@ struct drvtyp { int32 size; /* #blocks */ int32 devtype; /* device type */ int32 ctrl; /* ctrl type */ + char *name; /* device type name */ }; static struct drvtyp drv_tab[] = { - { RM03_SECT, RM03_SURF, RM03_CYL, RM03_SIZE, RM03_DEV, RM_CTRL }, - { RP04_SECT, RP04_SURF, RP04_CYL, RP04_SIZE, RP04_DEV, RP_CTRL }, - { RM80_SECT, RM80_SURF, RM80_CYL, RM80_SIZE, RM80_DEV, RM_CTRL }, - { RP06_SECT, RP06_SURF, RP06_CYL, RP06_SIZE, RP06_DEV, RP_CTRL }, - { RM05_SECT, RM05_SURF, RM05_CYL, RM05_SIZE, RM05_DEV, RM_CTRL }, - { RP07_SECT, RP07_SURF, RP07_CYL, RP07_SIZE, RP07_DEV, RM_CTRL }, + { RM03_SECT, RM03_SURF, RM03_CYL, RM03_SIZE, RM03_DEV, RM_CTRL, "RM03" }, + { RP04_SECT, RP04_SURF, RP04_CYL, RP04_SIZE, RP04_DEV, RP_CTRL, "RP04" }, + { RM80_SECT, RM80_SURF, RM80_CYL, RM80_SIZE, RM80_DEV, RM_CTRL, "RM80" }, + { RP06_SECT, RP06_SURF, RP06_CYL, RP06_SIZE, RP06_DEV, RP_CTRL, "RP06" }, + { RM05_SECT, RM05_SURF, RM05_CYL, RM05_SIZE, RM05_DEV, RM_CTRL, "RM05" }, + { RP07_SECT, RP07_SURF, RP07_CYL, RP07_SIZE, RP07_DEV, RM_CTRL, "RP07" }, { 0 } }; -uint16 *rpxb = NULL; /* xfer buffer */ +uint16 *rpxb[RP_NUMDR] = { 0 }; /* xfer buffer */ uint16 rpcs1[RP_NUMDR] = { 0 }; /* control/status 1 */ uint16 rpda[RP_NUMDR] = { 0 }; /* track/sector */ uint16 rpds[RP_NUMDR] = { 0 }; /* drive status */ @@ -346,7 +355,7 @@ uint16 rper3[RP_NUMDR] = { 0 }; /* error status 3 */ uint16 rpec1[RP_NUMDR] = { 0 }; /* ECC correction 1 */ uint16 rpec2[RP_NUMDR] = { 0 }; /* ECC correction 2 */ int32 rp_stopioe = 1; /* stop on error */ -int32 rp_swait = 10; /* seek time */ +int32 rp_swait = 26; /* seek time */ int32 rp_rwait = 10; /* rotate time */ static const char *rp_fname[CS1_N_FNC] = { "NOP", "UNLD", "SEEK", "RECAL", "DCLR", "RLS", "OFFS", "RETN", @@ -767,6 +776,15 @@ int32 rp_abort (void) return rp_reset (&rp_dev); } +/* I/O completion callback */ + +void rp_io_complete (UNIT *uptr, t_stat status) +{ +uptr->io_status = status; +uptr->io_complete = 1; +sim_activate (uptr, 0); +} + /* Service unit timeout Complete movement or data transfer command @@ -791,96 +809,127 @@ if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ rp_update_ds (DS_ATA, drv); /* set attn */ return (rp_stopioe? SCPE_UNATT: SCPE_OK); } -rpds[drv] = (rpds[drv] & ~DS_PIP) | DS_RDY; /* change drive status */ -switch (fnc) { /* case on function */ +if (!uptr->io_complete) { /* Top End (I/O Initiation) Processing */ + switch (fnc) { /* case on function */ - case FNC_OFFSET: /* offset */ - rp_update_ds (DS_OFM | DS_ATA, drv); - break; + case FNC_OFFSET: /* offset */ + rp_update_ds (DS_OFM | DS_ATA, drv); + break; - case FNC_RETURN: /* return to centerline */ - rpds[drv] = rpds[drv] & ~DS_OFM; /* clear offset, set attn */ - rp_update_ds (DS_ATA, drv); - break; + case FNC_RETURN: /* return to centerline */ + rpds[drv] = rpds[drv] & ~DS_OFM; /* clear offset, set attn */ + rp_update_ds (DS_ATA, drv); + break; - case FNC_UNLOAD: /* unload */ - rp_detach (uptr); /* detach unit */ - break; + case FNC_UNLOAD: /* unload */ + rp_detach (uptr); /* detach unit */ + break; - case FNC_RECAL: /* recalibrate */ - case FNC_SEARCH: /* search */ - case FNC_SEEK: /* seek */ - rp_update_ds (DS_ATA, drv); - break; + case FNC_RECAL: /* recalibrate */ + case FNC_SEARCH: /* search */ + case FNC_SEEK: /* seek */ + rp_update_ds (DS_ATA, drv); + break; - case FNC_WRITE: /* write */ - if (uptr->flags & UNIT_WPRT) { /* write locked? */ - rp_set_er (ER1_WLE, drv); /* set drive error */ - mba_set_exc (rp_dib.ba); /* set exception */ - rp_update_ds (DS_ATA, drv); /* set attn */ - return SCPE_OK; - } - case FNC_WCHK: /* write check */ - case FNC_READ: /* read */ - case FNC_READH: /* read headers */ - err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET); - mbc = mba_get_bc (rp_dib.ba); /* get byte count */ - wc = (mbc + 1) >> 1; /* convert to words */ - if ((da + wc) > drv_tab[dtype].size) { /* disk overrun? */ - rp_set_er (ER1_AOE, drv); /* set err */ - wc = drv_tab[dtype].size - da; /* trim xfer */ - mbc = wc << 1; /* trim mb count */ - if (da >= drv_tab[dtype].size) { /* none left? */ + case FNC_WRITE: /* write */ + if (uptr->flags & UNIT_WPRT) { /* write locked? */ + rp_set_er (ER1_WLE, drv); /* set drive error */ mba_set_exc (rp_dib.ba); /* set exception */ rp_update_ds (DS_ATA, drv); /* set attn */ - break; + return SCPE_OK; } - } - if (fnc == FNC_WRITE) { /* write? */ - abc = mba_rdbufW (rp_dib.ba, mbc, rpxb); /* get buffer */ - wc = (abc + 1) >> 1; /* actual # wds */ - awc = (wc + (RP_NUMWD - 1)) & ~(RP_NUMWD - 1); - for (i = wc; i < awc; i++) /* fill buf */ - rpxb[i] = 0; - if (wc && !err) { /* write buf */ - fxwrite (rpxb, sizeof (uint16), awc, uptr->fileref); - err = ferror (uptr->fileref); + case FNC_WCHK: /* write check */ + case FNC_READ: /* read */ + case FNC_READH: /* read headers */ + mbc = mba_get_bc (rp_dib.ba); /* get byte count */ + wc = (mbc + 1) >> 1; /* convert to words */ + if ((da + wc) > drv_tab[dtype].size) { /* disk overrun? */ + rp_set_er (ER1_AOE, drv); /* set err */ + wc = drv_tab[dtype].size - da; /* trim xfer */ + mbc = wc << 1; /* trim mb count */ + if (da >= drv_tab[dtype].size) { /* none left? */ + mba_set_exc (rp_dib.ba); /* set exception */ + rp_update_ds (DS_ATA, drv); /* set attn */ + break; + } } - } /* end if wr */ - else { /* read or wchk */ - awc = fxread (rpxb, sizeof (uint16), wc, uptr->fileref); - err = ferror (uptr->fileref); - for (i = awc; i < wc; i++) /* fill buf */ - rpxb[i] = 0; - if (fnc == FNC_WCHK) /* write check? */ - mba_chbufW (rp_dib.ba, mbc, rpxb); /* check vs mem */ - else mba_wrbufW (rp_dib.ba, mbc, rpxb); /* store in mem */ - } /* end if read */ - da = da + wc + (RP_NUMWD - 1); - if (da >= drv_tab[dtype].size) - rpds[drv] = rpds[drv] | DS_LST; - da = da / RP_NUMWD; - rpda[drv] = da % drv_tab[dtype].sect; - da = da / drv_tab[dtype].sect; - rpda[drv] = rpda[drv] | ((da % drv_tab[dtype].surf) << DA_V_SF); - rpdc[drv] = da / drv_tab[dtype].surf; - uptr->CYL = rpdc[drv]; + if (fnc == FNC_WRITE) { /* write? */ + abc = mba_rdbufW (rp_dib.ba, mbc, rpxb[drv]);/* get buffer */ + wc = (abc + 1) >> 1; /* actual # wds */ + awc = (wc + (RP_NUMWD - 1)) & ~(RP_NUMWD - 1); + for (i = wc; i < awc; i++) /* fill buf */ + rpxb[drv][i] = 0; + sim_disk_wrsect_a (uptr, da/RP_NUMWD, (void *)rpxb[drv], NULL, awc/RP_NUMWD, rp_io_complete); + return SCPE_OK; + } /* end if wr */ + else { /* read or wchk */ + awc = (wc + (RP_NUMWD - 1)) & ~(RP_NUMWD - 1); + sim_disk_rdsect_a (uptr, da/RP_NUMWD, (void *)rpxb[drv], (t_seccnt*)&uptr->sectsread, awc/RP_NUMWD, rp_io_complete); + return SCPE_OK; + } /* end if read */ - if (err != 0) { /* error? */ - rp_set_er (ER1_PAR, drv); /* set drive error */ - mba_set_exc (rp_dib.ba); /* set exception */ - rp_update_ds (DS_ATA, drv); - perror ("RP I/O error"); - clearerr (uptr->fileref); - return SCPE_IOERR; - } + case FNC_WRITEH: /* write headers stub */ + mba_set_don (rp_dib.ba); /* set done */ + rp_update_ds (0, drv); /* update ds */ + break; + } /* end case func */ + } +else { /* Bottom End (After I/O processing) */ + uptr->io_complete = 0; + err = uptr->io_status; - case FNC_WRITEH: /* write headers stub */ - mba_set_don (rp_dib.ba); /* set done */ - rp_update_ds (0, drv); /* update ds */ - break; - } /* end case func */ + switch (fnc) { /* case on function */ + + case FNC_OFFSET: /* offset */ + case FNC_RETURN: /* return to centerline */ + case FNC_UNLOAD: /* unload */ + case FNC_RECAL: /* recalibrate */ + case FNC_SEARCH: /* search */ + case FNC_SEEK: /* seek */ + case FNC_WRITEH: /* write headers stub */ + break; + + case FNC_WRITE: /* write */ + case FNC_WCHK: /* write check */ + case FNC_READ: /* read */ + case FNC_READH: /* read headers */ + mbc = mba_get_bc (rp_dib.ba); /* get byte count */ + wc = (mbc + 1) >> 1; /* convert to words */ + if (fnc == FNC_WRITE) { /* write? */ + } /* end if wr */ + else { /* read or wchk */ + awc = uptr->sectsread * RP_NUMWD; + for (i = awc; i < wc; i++) /* fill buf */ + rpxb[drv][i] = 0; + if (fnc == FNC_WCHK) /* write check? */ + mba_chbufW (rp_dib.ba, mbc, rpxb[drv]); /* check vs mem */ + else mba_wrbufW (rp_dib.ba, mbc, rpxb[drv]);/* store in mem */ + } /* end if read */ + da = da + wc + (RP_NUMWD - 1); + if (da >= drv_tab[dtype].size) + rpds[drv] = rpds[drv] | DS_LST; + da = da / RP_NUMWD; + rpda[drv] = da % drv_tab[dtype].sect; + da = da / drv_tab[dtype].sect; + rpda[drv] = rpda[drv] | ((da % drv_tab[dtype].surf) << DA_V_SF); + rpdc[drv] = da / drv_tab[dtype].surf; + uptr->CYL = rpdc[drv]; + + if (err != 0) { /* error? */ + rp_set_er (ER1_PAR, drv); /* set drive error */ + mba_set_exc (rp_dib.ba); /* set exception */ + rp_update_ds (DS_ATA, drv); + perror ("RP I/O error"); + return SCPE_IOERR; + } + + mba_set_don (rp_dib.ba); /* set done */ + rp_update_ds (0, drv); /* update ds */ + break; + } /* end case func */ + } +rpds[drv] = (rpds[drv] & ~DS_PIP) | DS_RDY; /* change drive status */ if (DEBUG_PRS (rp_dev)) fprintf (sim_deb, ">>RP%d DONE: fnc=%s, ds=%o, cyl=%o, da=%o, er=%d\n", @@ -963,11 +1012,11 @@ for (i = 0; i < RP_NUMDR; i++) { rpec2[i] = 0; rmmr2[i] = 0; rmhr[i] = 0; + if (rpxb[i] == NULL) + rpxb[i] = (uint16 *) calloc (RP_MAXFR, sizeof (uint16)); + if (rpxb[i] == NULL) + return SCPE_MEM; } -if (rpxb == NULL) - rpxb = (uint16 *) calloc (RP_MAXFR, sizeof (uint16)); -if (rpxb == NULL) - return SCPE_MEM; return SCPE_OK; } @@ -979,7 +1028,9 @@ int32 drv, i, p; t_stat r; uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; -r = attach_unit (uptr, cptr); /* attach unit */ +r = sim_disk_attach (uptr, cptr, RP_NUMWD * sizeof (uint16), + sizeof (uint16), TRUE, 0, + drv_tab[GET_DTYPE (uptr->flags)].name, drv_tab[GET_DTYPE (uptr->flags)].sect); if (r != SCPE_OK) /* error? */ return r; drv = (int32) (uptr - rp_dev.units); /* get drv number */ @@ -988,14 +1039,9 @@ rpds[drv] = DS_MOL | DS_RDY | DS_DPR | /* upd drv status */ rper1[drv] = 0; rp_update_ds (DS_ATA, drv); /* upd ctlr status */ -if ((p = sim_fsize (uptr->fileref)) == 0) { /* new disk image? */ - if (uptr->flags & UNIT_RO) - return SCPE_OK; - return pdp11_bad_block (uptr, - drv_tab[GET_DTYPE (uptr->flags)].sect, RP_NUMWD); - } if ((uptr->flags & UNIT_AUTO) == 0) /* autosize? */ return SCPE_OK; +p = (int32)sim_disk_size (uptr); for (i = 0; drv_tab[i].sect != 0; i++) { if (p <= (drv_tab[i].size * (int) sizeof (int16))) { uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); @@ -1017,7 +1063,7 @@ if (!(uptr->flags & UNIT_ATT)) /* attached? */ drv = (int32) (uptr - rp_dev.units); /* get drv number */ rpds[drv] = rpds[drv] & ~(DS_MOL | DS_RDY | DS_WRL | DS_VV | DS_OFM); rp_update_ds (DS_ATA, drv); /* request intr */ -return detach_unit (uptr); +return sim_disk_detach (uptr); } /* Set size command validation routine */ diff --git a/PDP11/pdp11_rq.c b/PDP11/pdp11_rq.c index 89da0cb5..8bd6044e 100644 --- a/PDP11/pdp11_rq.c +++ b/PDP11/pdp11_rq.c @@ -26,6 +26,19 @@ rq RQDX3 disk controller + 07-Mar-11 MP Added working behaviors for removable device types. + This allows physical CDROM's to come online and be + ejected. + 02-Mar-11 MP Fixed missing information from save/restore which + caused operations to not complete correctly after + a restore until the OS reset the controller. + 02-Feb-11 MP Added Autosize support to rq_attach + 28-Jan-11 MP Adopted use of sim_disk disk I/O library + - added support for the multiple formats sim_disk + provides (SimH, RAW, and VHD) + - adjusted to potentially leverage asynch I/O when + available + - Added differing detailed debug output via sim_debug 14-Jan-09 JH Added support for RD32 disc drive 18-Jun-07 RMS Added UNIT_IDLE flag to timer thread 31-Oct-05 RMS Fixed address width for large files @@ -97,6 +110,7 @@ extern int32 cpu_opt; #include "pdp11_uqssp.h" #include "pdp11_mscp.h" +#include "sim_disk.h" #define UF_MSK (UF_CMR|UF_CMW) /* settable flags */ @@ -129,20 +143,25 @@ extern int32 cpu_opt; #define UNIT_V_ATP (UNIT_V_UF + 2) /* attn pending */ #define UNIT_V_DTYPE (UNIT_V_UF + 3) /* drive type */ #define UNIT_M_DTYPE 0x1F +#define UNIT_V_NOAUTO (UNIT_V_UF + 8) /* noautosize */ #define UNIT_ONL (1 << UNIT_V_ONL) #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_ATP (1 << UNIT_V_ATP) +#define UNIT_NOAUTO (1 << UNIT_V_NOAUTO) #define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) #define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) #define cpkt u3 /* current packet */ #define pktq u4 /* packet queue */ #define uf buf /* settable unit flags */ #define cnum wait /* controller index */ +#define io_status u5 /* io status from callback */ +#define io_complete u6 /* io completion flag */ +#define rqxb filebuf /* xfer buffer */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ #define RQ_RMV(u) ((drv_tab[GET_DTYPE (u->flags)].flgs & RQDF_RMV)? \ UF_RMV: 0) #define RQ_WPH(u) (((drv_tab[GET_DTYPE (u->flags)].flgs & RQDF_RO) || \ - (u->flags & UNIT_WPRT))? UF_WPH: 0) + (u->flags & UNIT_WPRT) || sim_disk_wrp (u))? UF_WPH: 0) #define CST_S1 0 /* init stage 1 */ #define CST_S1_WR 1 /* stage 1 wrap */ @@ -567,7 +586,6 @@ extern FILE *sim_deb; extern uint32 sim_taddr_64; extern int32 sim_switches; -uint16 *rqxb = NULL; /* xfer buffer */ int32 rq_itime = 200; /* init time, except */ int32 rq_itime4 = 10; /* stage 4 */ int32 rq_qtime = RQ_QTIME; /* queue time */ @@ -597,7 +615,56 @@ typedef struct { struct rqpkt pak[RQ_NPKTS]; /* packet queue */ } MSC; -DEVICE rq_dev, rqb_dev, rqc_dev,rqd_dev; +/* debugging bitmaps */ +#define DBG_TRC 0x0001 /* trace routine calls */ +#define DBG_INI 0x0002 /* display setup/init sequence info */ +#define DBG_REG 0x0004 /* trace read/write registers */ +#define DBG_REQ 0x0008 /* display transfer requests */ +#define DBG_DSK 0x0010 /* display sim_disk activities */ +#define DBG_DAT 0x0020 /* display transfer data */ + +DEBTAB rq_debug[] = { + {"TRACE", DBG_TRC}, + {"INIT", DBG_INI}, + {"REG", DBG_REG}, + {"REQ", DBG_REQ}, + {"DISK", DBG_DSK}, + {"DATA", DBG_DAT}, + {0} +}; + +static char *rq_cmdname[] = { + "", /* 0 */ + "ABO", /* 1 b: abort */ + "GCS", /* 2 b: get command status */ + "GUS", /* 3 b: get unit status */ + "SCC", /* 4 b: set controller char */ + "","","", /* 5-7 */ + "AVL", /* 8 b: available */ + "ONL", /* 9 b: online */ + "SUC", /* 10 b: set unit char */ + "DAP", /* 11 b: det acc paths - nop */ + "","","","", /* 12-15 */ + "ACC", /* 16 b: access */ + "CCD", /* 17 d: compare - nop */ + "ERS", /* 18 b: erase */ + "FLU", /* 19 d: flush - nop */ + "","", /* 20-21 */ + "ERG", /* 22 t: erase gap */ + "","","","","","","","","", /* 23-31 */ + "CMP", /* 32 b: compare */ + "RD", /* 33 b: read */ + "WR", /* 34 b: write */ + "", /* 35 */ + "WTM", /* 36 t: write tape mark */ + "POS", /* 37 t: reposition */ + "","","","","","","","","", /* 38-46 */ + "FMT", /* 47 d: format */ + "","","","","","","","","","","","","","","","", /* 48-63 */ + "AVA", /* 64 b: unit now avail */ + }; + +DEVICE rq_dev, rqb_dev, rqc_dev, rqd_dev; t_stat rq_rd (int32 *data, int32 PA, int32 access); t_stat rq_wr (int32 data, int32 PA, int32 access); @@ -686,9 +753,11 @@ REG rq_reg[] = { { GRDATA (SAW, rq_ctx.saw, DEV_RDX, 16, 0) }, { GRDATA (S1DAT, rq_ctx.s1dat, DEV_RDX, 16, 0) }, { GRDATA (COMM, rq_ctx.comm, DEV_RDX, 22, 0) }, + { GRDATA (CQIOFF, rq_ctx.cq.ioff, DEV_RDX, 32, 0) }, { GRDATA (CQBA, rq_ctx.cq.ba, DEV_RDX, 22, 0) }, { GRDATA (CQLNT, rq_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (CQIDX, rq_ctx.cq.idx, DEV_RDX, 8, 2) }, + { GRDATA (RQIOFF, rq_ctx.rq.ioff, DEV_RDX, 32, 0) }, { GRDATA (RQBA, rq_ctx.rq.ba, DEV_RDX, 22, 0) }, { GRDATA (RQLNT, rq_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (RQIDX, rq_ctx.rq.idx, DEV_RDX, 8, 2) }, @@ -774,6 +843,10 @@ MTAB rq_mod[] = { &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, 0, "TYPE", NULL, NULL, &rq_show_type, NULL }, + { UNIT_NOAUTO, UNIT_NOAUTO, "noautosize", "NOAUTOSIZE", NULL }, + { UNIT_NOAUTO, 0, "autosize", "AUTOSIZE", NULL }, + { MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_disk_set_fmt, &sim_disk_show_fmt, NULL }, #if defined (VM_PDP11) { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, @@ -793,7 +866,8 @@ DEVICE rq_dev = { RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, NULL, NULL, &rq_reset, &rq_boot, &rq_attach, &rq_detach, - &rq_dib, DEV_FLTA | DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG + &rq_dib, DEV_FLTA | DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG, + 0, rq_debug }; /* RQB data structures @@ -829,9 +903,11 @@ REG rqb_reg[] = { { GRDATA (SAW, rqb_ctx.saw, DEV_RDX, 16, 0) }, { GRDATA (S1DAT, rqb_ctx.s1dat, DEV_RDX, 16, 0) }, { GRDATA (COMM, rqb_ctx.comm, DEV_RDX, 22, 0) }, + { GRDATA (CQIOFF, rqb_ctx.cq.ioff, DEV_RDX, 32, 0) }, { GRDATA (CQBA, rqb_ctx.cq.ba, DEV_RDX, 22, 0) }, { GRDATA (CQLNT, rqb_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (CQIDX, rqb_ctx.cq.idx, DEV_RDX, 8, 2) }, + { GRDATA (RQIOFF, rqb_ctx.rq.ioff, DEV_RDX, 32, 0) }, { GRDATA (RQBA, rqb_ctx.rq.ba, DEV_RDX, 22, 0) }, { GRDATA (RQLNT, rqb_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (RQIDX, rqb_ctx.rq.idx, DEV_RDX, 8, 2) }, @@ -851,7 +927,7 @@ REG rqb_reg[] = { { URDATA (CPKT, rqb_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (PKTQ, rqb_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (UFLG, rqb_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, - { URDATA (CAPAC, rqb_unit[0].capac, 10, 31, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, + { URDATA (CAPAC, rqb_unit[0].capac, 10, T_ADDR_W, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, { GRDATA (DEVADDR, rqb_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, rqb_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } @@ -862,7 +938,8 @@ DEVICE rqb_dev = { RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, NULL, NULL, &rq_reset, &rq_boot, &rq_attach, &rq_detach, - &rqb_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG + &rqb_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG, + 0, rq_debug }; /* RQC data structures @@ -898,9 +975,11 @@ REG rqc_reg[] = { { GRDATA (SAW, rqc_ctx.saw, DEV_RDX, 16, 0) }, { GRDATA (S1DAT, rqc_ctx.s1dat, DEV_RDX, 16, 0) }, { GRDATA (COMM, rqc_ctx.comm, DEV_RDX, 22, 0) }, + { GRDATA (CQIOFF, rqc_ctx.cq.ioff, DEV_RDX, 32, 0) }, { GRDATA (CQBA, rqc_ctx.cq.ba, DEV_RDX, 22, 0) }, { GRDATA (CQLNT, rqc_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (CQIDX, rqc_ctx.cq.idx, DEV_RDX, 8, 2) }, + { GRDATA (RQIOFF, rqc_ctx.rq.ioff, DEV_RDX, 32, 0) }, { GRDATA (RQBA, rqc_ctx.rq.ba, DEV_RDX, 22, 0) }, { GRDATA (RQLNT, rqc_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (RQIDX, rqc_ctx.rq.idx, DEV_RDX, 8, 2) }, @@ -920,7 +999,7 @@ REG rqc_reg[] = { { URDATA (CPKT, rqc_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (PKTQ, rqc_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (UFLG, rqc_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, - { URDATA (CAPAC, rqc_unit[0].capac, 10, 31, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, + { URDATA (CAPAC, rqc_unit[0].capac, 10, T_ADDR_W, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, { GRDATA (DEVADDR, rqc_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, rqc_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } @@ -931,7 +1010,8 @@ DEVICE rqc_dev = { RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, NULL, NULL, &rq_reset, &rq_boot, &rq_attach, &rq_detach, - &rqc_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG + &rqc_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG, + 0, rq_debug }; /* RQD data structures @@ -967,9 +1047,11 @@ REG rqd_reg[] = { { GRDATA (SAW, rqd_ctx.saw, DEV_RDX, 16, 0) }, { GRDATA (S1DAT, rqd_ctx.s1dat, DEV_RDX, 16, 0) }, { GRDATA (COMM, rqd_ctx.comm, DEV_RDX, 22, 0) }, + { GRDATA (CQIOFF, rqd_ctx.cq.ioff, DEV_RDX, 32, 0) }, { GRDATA (CQBA, rqd_ctx.cq.ba, DEV_RDX, 22, 0) }, { GRDATA (CQLNT, rqd_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (CQIDX, rqd_ctx.cq.idx, DEV_RDX, 8, 2) }, + { GRDATA (RQIOFF, rqd_ctx.rq.ioff, DEV_RDX, 32, 0) }, { GRDATA (RQBA, rqd_ctx.rq.ba, DEV_RDX, 22, 0) }, { GRDATA (RQLNT, rqd_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (RQIDX, rqd_ctx.rq.idx, DEV_RDX, 8, 2) }, @@ -989,7 +1071,7 @@ REG rqd_reg[] = { { URDATA (CPKT, rqd_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (PKTQ, rqd_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (UFLG, rqd_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, - { URDATA (CAPAC, rqd_unit[0].capac, 10, 31, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, + { URDATA (CAPAC, rqd_unit[0].capac, 10, T_ADDR_W, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, { GRDATA (DEVADDR, rqd_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, rqd_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } @@ -1000,7 +1082,8 @@ DEVICE rqd_dev = { RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, NULL, NULL, &rq_reset, &rq_boot, &rq_attach, &rq_detach, - &rqd_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG + &rqd_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG, + 0, rq_debug }; static DEVICE *rq_devmap[RQ_NUMCT] = { @@ -1023,6 +1106,8 @@ int32 cidx = rq_map_pa ((uint32) PA); MSC *cp = rq_ctxmap[cidx]; DEVICE *dptr = rq_devmap[cidx]; +sim_debug(DBG_REG, dptr, "rq_rd(PA=0x%08X [%s], access=%d)\n", PA, ((PA >> 1) & 01) ? "IP" : "SA", access); + if (cidx < 0) return SCPE_IERR; switch ((PA >> 1) & 01) { /* decode PA<1> */ @@ -1032,9 +1117,7 @@ switch ((PA >> 1) & 01) { /* decode PA<1> */ if (cp->csta == CST_S3_PPB) /* waiting for poll? */ rq_step4 (cp); else if (cp->csta == CST_UP) { /* if up */ - if (DEBUG_PRD (dptr)) - fprintf (sim_deb, ">>RQ%c: poll started, PC=%X\n", - 'A' + cp->cnum, OLDPC); + sim_debug (DBG_REQ, dptr, "poll started, PC=%X\n", OLDPC); cp->pip = 1; /* poll host */ sim_activate (dptr->units + RQ_QUEUE, rq_qtime); } @@ -1044,7 +1127,6 @@ switch ((PA >> 1) & 01) { /* decode PA<1> */ *data = cp->sa; break; } - return SCPE_OK; } @@ -1056,13 +1138,14 @@ DEVICE *dptr = rq_devmap[cidx]; if (cidx < 0) return SCPE_IERR; + +sim_debug(DBG_REG, dptr, "rq_wr(PA=0x%08X [%s], access=%d)\n", PA, ((PA >> 1) & 01) ? "IP" : "SA", access); + switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* IP */ rq_reset (rq_devmap[cidx]); /* init device */ - if (DEBUG_PRD (dptr)) - fprintf (sim_deb, ">>RQ%c: initialization started\n", - 'A' + cp->cnum); + sim_debug (DBG_REQ, dptr, "initialization started\n"); break; case 1: /* SA */ @@ -1147,7 +1230,12 @@ MSC *cp = rq_ctxmap[uptr->cnum]; DEVICE *dptr = rq_devmap[uptr->cnum]; DIB *dibp = (DIB *) dptr->ctxt; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_quesvc\n"); + if (cp->csta < CST_UP) { /* still init? */ + + sim_debug(DBG_INI, dptr, "CSTA=%d, SAW=0x%X\n", cp->csta, cp->saw); + switch (cp->csta) { /* controller state? */ case CST_S1: /* need S1 reply */ @@ -1197,8 +1285,7 @@ if (cp->csta < CST_UP) { /* still init? */ case CST_S4: /* need S4 reply */ if (cp->saw & SA_S4H_GO) { /* go set? */ - if (DEBUG_PRD (dptr)) - fprintf (sim_deb, ">>RQ%c: initialization complete\n", 'A' + cp->cnum); + sim_debug (DBG_REQ, dptr, "initialization complete\n"); cp->csta = CST_UP; /* we're up */ cp->sa = 0; /* clear SA */ sim_activate (dptr->units + RQ_TIMER, tmr_poll * clk_tps); @@ -1224,15 +1311,12 @@ if ((pkt == 0) && cp->pip) { /* polling? */ if (!rq_getpkt (cp, &pkt)) /* get host pkt */ return SCPE_OK; if (pkt) { /* got one? */ - if (DEBUG_PRD (dptr)) { - fprintf (sim_deb, ">>RQ%c: cmd=%04X, mod=%04X, unit=%d, ", - 'A' + cp->cnum, cp->pak[pkt].d[CMD_OPC], - cp->pak[pkt].d[CMD_MOD], cp->pak[pkt].d[CMD_UN]); - fprintf (sim_deb, "bc=%04X%04X, ma=%04X%04X, lbn=%04X%04X\n", + sim_debug (DBG_REQ, dptr, "cmd=%04X(%3s), mod=%04X, unit=%d, bc=%04X%04X, ma=%04X%04X, lbn=%04X%04X\n", + cp->pak[pkt].d[CMD_OPC], rq_cmdname[cp->pak[pkt].d[CMD_OPC]&0x3f], + cp->pak[pkt].d[CMD_MOD], cp->pak[pkt].d[CMD_UN], cp->pak[pkt].d[RW_BCH], cp->pak[pkt].d[RW_BCL], cp->pak[pkt].d[RW_BAH], cp->pak[pkt].d[RW_BAL], cp->pak[pkt].d[RW_LBNH], cp->pak[pkt].d[RW_LBNL]); - } if (GETP (pkt, UQ_HCTC, TYP) != UQ_TYP_SEQ) /* seq packet? */ return rq_fatal (cp, PE_PIE); /* no, term thread */ cnid = GETP (pkt, UQ_HCTC, CID); /* get conn ID */ @@ -1253,6 +1337,7 @@ if (cp->rspq) { /* resp q? */ pkt = rq_deqh (cp, &cp->rspq); /* get top of q */ if (!rq_putpkt (cp, pkt, FALSE)) /* send to host */ return SCPE_OK; + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_quesvc - rq_putpkt failed - 1\n"); } /* end if resp q */ if (pkt) /* more to do? */ sim_activate (uptr, rq_qtime); @@ -1268,6 +1353,7 @@ UNIT *nuptr; MSC *cp = rq_ctxmap[uptr->cnum]; DEVICE *dptr = rq_devmap[uptr->cnum]; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_tmrsvc\n"); sim_activate (uptr, tmr_poll * clk_tps); /* reactivate */ for (i = 0; i < RQ_NUMDR; i++) { /* poll */ nuptr = dptr->units + i; @@ -1290,6 +1376,8 @@ t_bool rq_mscp (MSC *cp, int32 pkt, t_bool q) { uint32 sts, cmd = GETP (pkt, CMD_OPC, OPC); +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_mscp - %s\n", q? "Queue" : "No Queue"); + switch (cmd) { case OP_ABO: /* abort */ @@ -1351,6 +1439,8 @@ int32 tpkt, prv; UNIT *uptr; DEVICE *dptr = rq_devmap[cp->cnum]; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_abo\n"); + tpkt = 0; /* set no mtch */ if (uptr = rq_getucb (cp, lu)) { /* get unit */ if (uptr->cpkt && /* curr pkt? */ @@ -1390,15 +1480,20 @@ t_bool rq_avl (MSC *cp, int32 pkt, t_bool q) { uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ +uint32 mdf = cp->pak[pkt].d[CMD_MOD]; /* modifier */ uint32 sts; UNIT *uptr; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_avl\n"); + if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ if (q && uptr->cpkt) { /* need to queue? */ rq_enqt (cp, &uptr->pktq, pkt); /* do later */ return OK; } uptr->flags = uptr->flags & ~UNIT_ONL; /* not online */ + if ((mdf & MD_SPD) && RQ_RMV (uptr)) /* unload of removable device */ + sim_disk_unload (uptr); uptr->uf = 0; /* clr flags */ sts = ST_SUC; /* success */ } @@ -1417,6 +1512,8 @@ uint32 ref = GETP32 (pkt, GCS_REFL); /* ref # */ int32 tpkt; UNIT *uptr; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_gcs\n"); + if ((uptr = rq_getucb (cp, lu)) && /* valid lu? */ (tpkt = uptr->cpkt) && /* queued pkt? */ (GETP32 (tpkt, CMD_REFL) == ref) && /* match ref? */ @@ -1441,6 +1538,8 @@ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 dtyp, sts, rbpar; UNIT *uptr; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_gus\n"); + if (cp->pak[pkt].d[CMD_MOD] & MD_NXU) { /* next unit? */ if (lu >= (cp->ubase + RQ_NUMDR)) { /* end of range? */ lu = 0; /* reset to 0 */ @@ -1482,6 +1581,8 @@ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 sts; UNIT *uptr; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_onl\n"); + if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ if (q && uptr->cpkt) { /* need to queue? */ rq_enqt (cp, &uptr->pktq, pkt); /* do later */ @@ -1491,11 +1592,14 @@ if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ sts = ST_OFL | SB_OFL_NV; /* offl no vol */ else if (uptr->flags & UNIT_ONL) /* already online? */ sts = ST_SUC | SB_SUC_ON; - else { /* mark online */ + else if (sim_disk_isavailable (uptr)) + { /* mark online */ sts = ST_SUC; uptr->flags = uptr->flags | UNIT_ONL; rq_setf_unit (cp, pkt, uptr); /* hack flags */ } + else + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ rq_putr_unit (cp, pkt, uptr, lu, TRUE); /* set fields */ } else sts = ST_OFL; /* offline */ @@ -1511,6 +1615,8 @@ t_bool rq_scc (MSC *cp, int32 pkt, t_bool q) { int32 sts, cmd; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_scc\n"); + if (cp->pak[pkt].d[SCC_MSV]) { /* MSCP ver = 0? */ sts = ST_CMD | I_VRSN; /* no, lose */ cmd = 0; @@ -1547,6 +1653,8 @@ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 sts; UNIT *uptr; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_suc\n"); + if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ if (q && uptr->cpkt) { /* need to queue? */ rq_enqt (cp, &uptr->pktq, pkt); /* do later */ @@ -1576,6 +1684,8 @@ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 sts; UNIT *uptr; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_fmt\n"); + if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ if (q && uptr->cpkt) { /* need to queue? */ rq_enqt (cp, &uptr->pktq, pkt); /* do later */ @@ -1610,8 +1720,11 @@ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 sts; UNIT *uptr; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_rw(lu=%d, pkt=%d, queue=%s)\n", lu, pkt, q?"yes" : "no"); + if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ if (q && uptr->cpkt) { /* need to queue? */ + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_rw - queued\n"); rq_enqt (cp, &uptr->pktq, pkt); /* do later */ return OK; } @@ -1624,7 +1737,9 @@ if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ cp->pak[pkt].d[RW_WBCH] = cp->pak[pkt].d[RW_BCH]; cp->pak[pkt].d[RW_WBLL] = cp->pak[pkt].d[RW_LBNL]; cp->pak[pkt].d[RW_WBLH] = cp->pak[pkt].d[RW_LBNH]; - sim_activate (uptr, rq_xtime); /* activate */ + uptr->iostarttime = sim_grtime(); + sim_activate (uptr, 0); /* activate */ + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_rw - started\n"); return OK; /* done */ } } @@ -1674,12 +1789,28 @@ if ((cmd == OP_WR) || (cmd == OP_ERS)) { /* write op? */ return 0; /* success! */ } +/* I/O completion callback */ + +void rq_io_complete (UNIT *uptr, t_stat status) +{ +MSC *cp = rq_ctxmap[uptr->cnum]; +int32 elapsed = sim_grtime()-uptr->iostarttime; + +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_io_complete(status=%d)\n", status); + +uptr->io_status = status; +uptr->io_complete = 1; +if (elapsed > rq_xtime) + sim_activate (uptr, 0); +else + sim_activate (uptr, rq_xtime-elapsed); +} + /* Unit service for data transfer commands */ t_stat rq_svc (UNIT *uptr) { MSC *cp = rq_ctxmap[uptr->cnum]; - uint32 i, t, tbc, abc, wwc; uint32 err = 0; int32 pkt = uptr->cpkt; /* get packet */ @@ -1687,7 +1818,10 @@ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ uint32 ba = GETP32 (pkt, RW_WBAL); /* buf addr */ uint32 bc = GETP32 (pkt, RW_WBCL); /* byte count */ uint32 bl = GETP32 (pkt, RW_WBLL); /* block addr */ -t_addr da = ((t_addr) bl) * RQ_NUMBY; /* disk addr */ + +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_svc(unit=%d, pkt=%d, cmd=%s, lbn=%0X, bc=%0x, phase=%s)\n", + uptr-rq_devmap[cp->cnum]->units, pkt, rq_cmdname[cp->pak[pkt].d[CMD_OPC]&0x3f], bl, bc, + uptr->io_complete ? "bottom" : "top"); if ((cp == NULL) || (pkt == 0)) /* what??? */ return STOP_RQ; @@ -1713,77 +1847,85 @@ if ((cmd == OP_ERS) || (cmd == OP_WR)) { /* write op? */ } } -if (cmd == OP_ERS) { /* erase? */ - wwc = ((tbc + (RQ_NUMBY - 1)) & ~(RQ_NUMBY - 1)) >> 1; - for (i = 0; i < wwc; i++) /* clr buf */ - rqxb[i] = 0; - err = sim_fseek (uptr->fileref, da, SEEK_SET); /* set pos */ - if (!err) - sim_fwrite (rqxb, sizeof (int16), wwc, uptr->fileref); - err = ferror (uptr->fileref); /* end if erase */ - } +if (!uptr->io_complete) { /* Top End (I/O Initiation) Processing */ + if (cmd == OP_ERS) { /* erase? */ + wwc = ((tbc + (RQ_NUMBY - 1)) & ~(RQ_NUMBY - 1)) >> 1; + memset (uptr->rqxb, 0, wwc * sizeof(uint16)); /* clr buf */ + sim_disk_data_trace(uptr, uptr->rqxb, bl, wwc << 1, "sim_disk_wrsect-ERS", DBG_DAT & rq_devmap[cp->cnum]->dctrl, DBG_REQ); + err = sim_disk_wrsect_a (uptr, bl, uptr->rqxb, NULL, (wwc << 1) / RQ_NUMBY, rq_io_complete); + } -else if (cmd == OP_WR) { /* write? */ - t = Map_ReadW (ba, tbc, rqxb); /* fetch buffer */ - if (abc = tbc - t) { /* any xfer? */ - wwc = ((abc + (RQ_NUMBY - 1)) & ~(RQ_NUMBY - 1)) >> 1; - for (i = (abc >> 1); i < wwc; i++) - rqxb[i] = 0; - err = sim_fseek (uptr->fileref, da, SEEK_SET); - if (!err) - sim_fwrite (rqxb, sizeof (int16), wwc, uptr->fileref); - err = ferror (uptr->fileref); - } - if (t) { /* nxm? */ - PUTP32 (pkt, RW_WBCL, bc - abc); /* adj bc */ - PUTP32 (pkt, RW_WBAL, ba + abc); /* adj ba */ - if (rq_hbe (cp, uptr)) /* post err log */ - rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); - return SCPE_OK; /* end else wr */ - } - } - -else { - err = sim_fseek (uptr->fileref, da, SEEK_SET); /* set pos */ - if (!err) { - i = sim_fread (rqxb, sizeof (int16), tbc >> 1, uptr->fileref); - for ( ; i < (tbc >> 1); i++) /* fill */ - rqxb[i] = 0; - err = ferror (uptr->fileref); - } - if ((cmd == OP_RD) && !err) { /* read? */ - if (t = Map_WriteW (ba, tbc, rqxb)) { /* store, nxm? */ - PUTP32 (pkt, RW_WBCL, bc - (tbc - t)); /* adj bc */ - PUTP32 (pkt, RW_WBAL, ba + (tbc - t)); /* adj ba */ - if (rq_hbe (cp, uptr)) /* post err log */ - rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); - return SCPE_OK; + else if (cmd == OP_WR) { /* write? */ + t = Map_ReadW (ba, tbc, uptr->rqxb); /* fetch buffer */ + if (abc = tbc - t) { /* any xfer? */ + wwc = ((abc + (RQ_NUMBY - 1)) & ~(RQ_NUMBY - 1)) >> 1; + for (i = (abc >> 1); i < wwc; i++) + ((uint16 *)(uptr->rqxb))[i] = 0; + sim_disk_data_trace(uptr, uptr->rqxb, bl, wwc << 1, "sim_disk_wrsect-WR", DBG_DAT & rq_devmap[cp->cnum]->dctrl, DBG_REQ); + err = sim_disk_wrsect_a (uptr, bl, uptr->rqxb, NULL, (wwc << 1) / RQ_NUMBY, rq_io_complete); } } - else if ((cmd == OP_CMP) && !err) { /* compare? */ - uint8 dby, mby; - for (i = 0; i < tbc; i++) { /* loop */ - if (Map_ReadB (ba + i, 1, &mby)) { /* fetch, nxm? */ - PUTP32 (pkt, RW_WBCL, bc - i); /* adj bc */ - PUTP32 (pkt, RW_WBAL, bc - i); /* adj ba */ + + else { /* OP_RD & OP_CMP */ + err = sim_disk_rdsect_a (uptr, bl, uptr->rqxb, NULL, (tbc + RQ_NUMBY - 1) / RQ_NUMBY, rq_io_complete); + } /* end else read */ + return SCPE_OK; /* done for now until callback */ + } +else { /* Bottom End (After I/O processing) */ + uptr->io_complete = 0; + err = uptr->io_status; + if (cmd == OP_ERS) { /* erase? */ + } + + else if (cmd == OP_WR) { /* write? */ + t = Map_ReadW (ba, tbc, uptr->rqxb); /* fetch buffer */ + abc = tbc - t; /* any xfer? */ + if (t) { /* nxm? */ + PUTP32 (pkt, RW_WBCL, bc - abc); /* adj bc */ + PUTP32 (pkt, RW_WBAL, ba + abc); /* adj ba */ + if (rq_hbe (cp, uptr)) /* post err log */ + rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); + return SCPE_OK; /* end else wr */ + } + } + + else { + sim_disk_data_trace(uptr, uptr->rqxb, bl, tbc, "sim_disk_rdsect", DBG_DAT & rq_devmap[cp->cnum]->dctrl, DBG_REQ); + if ((cmd == OP_RD) && !err) { /* read? */ + if (t = Map_WriteW (ba, tbc, uptr->rqxb)) { /* store, nxm? */ + PUTP32 (pkt, RW_WBCL, bc - (tbc - t)); /* adj bc */ + PUTP32 (pkt, RW_WBAL, ba + (tbc - t)); /* adj ba */ if (rq_hbe (cp, uptr)) /* post err log */ - rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); + rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); return SCPE_OK; } - dby = (rqxb[i >> 1] >> ((i & 1)? 8: 0)) & 0xFF; - if (mby != dby) { /* cmp err? */ - PUTP32 (pkt, RW_WBCL, bc - i); /* adj bc */ - rq_rw_end (cp, uptr, 0, ST_CMP); /* done */ - return SCPE_OK; /* exit */ - } /* end if */ - } /* end for */ - } /* end else if */ - } /* end else read */ + } + else if ((cmd == OP_CMP) && !err) { /* compare? */ + uint8 dby, mby; + for (i = 0; i < tbc; i++) { /* loop */ + if (Map_ReadB (ba + i, 1, &mby)) { /* fetch, nxm? */ + PUTP32 (pkt, RW_WBCL, bc - i); /* adj bc */ + PUTP32 (pkt, RW_WBAL, bc - i); /* adj ba */ + if (rq_hbe (cp, uptr)) /* post err log */ + rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); + return SCPE_OK; + } + dby = (((uint16 *)(uptr->rqxb))[i >> 1] >> ((i & 1)? 8: 0)) & 0xFF; + if (mby != dby) { /* cmp err? */ + PUTP32 (pkt, RW_WBCL, bc - i); /* adj bc */ + rq_rw_end (cp, uptr, 0, ST_CMP); /* done */ + return SCPE_OK; /* exit */ + } /* end if */ + } /* end for */ + } /* end else if */ + } /* end else read */ + } /* end else bottom end */ if (err != 0) { /* error? */ if (rq_dte (cp, uptr, ST_DRV)) /* post err log */ rq_rw_end (cp, uptr, EF_LOG, ST_DRV); /* if ok, report err */ perror ("RQ I/O error"); - clearerr (uptr->fileref); + if (!(uptr->flags | UNIT_RAW)) + clearerr (uptr->fileref); return SCPE_IOERR; } ba = ba + tbc; /* incr bus addr */ @@ -1793,7 +1935,7 @@ PUTP32 (pkt, RW_WBAL, ba); /* update pkt */ PUTP32 (pkt, RW_WBCL, bc); PUTP32 (pkt, RW_WBLL, bl); if (bc) /* more? resched */ - sim_activate (uptr, rq_xtime); + sim_activate (uptr, 0); else rq_rw_end (cp, uptr, 0, ST_SUC); /* done! */ return SCPE_OK; } @@ -1808,6 +1950,8 @@ uint32 bc = GETP32 (pkt, RW_BCL); /* init bc */ uint32 wbc = GETP32 (pkt, RW_WBCL); /* work bc */ DEVICE *dptr = rq_devmap[uptr->cnum]; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_rw_end\n"); + uptr->cpkt = 0; /* done */ PUTP32 (pkt, RW_BCL, bc - wbc); /* bytes processed */ cp->pak[pkt].d[RW_WBAL] = 0; /* clear temps */ @@ -1831,6 +1975,8 @@ t_bool rq_dte (MSC *cp, UNIT *uptr, uint32 err) int32 pkt, tpkt; uint32 lu, dtyp, lbn, ccyl, csurf, csect, t; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_dte\n"); + if ((cp->cflgs & CF_THS) == 0) /* logging? */ return OK; if (!rq_deqf (cp, &pkt)) /* get log pkt */ @@ -1883,6 +2029,8 @@ t_bool rq_hbe (MSC *cp, UNIT *uptr) { int32 pkt, tpkt; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_hbe\n"); + if ((cp->cflgs & CF_THS) == 0) /* logging? */ return OK; if (!rq_deqf (cp, &pkt)) /* get log pkt */ @@ -1912,6 +2060,8 @@ t_bool rq_plf (MSC *cp, uint32 err) { int32 pkt; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_plf\n"); + if (!rq_deqf (cp, &pkt)) /* get log pkt */ return ERR; cp->pak[pkt].d[ELP_REFL] = 0; /* ref = 0 */ @@ -1933,12 +2083,13 @@ return rq_putpkt (cp, pkt, TRUE); /* Unit now available attention packet */ -int32 rq_una (MSC *cp, int32 un) +t_bool rq_una (MSC *cp, int32 un) { int32 pkt; uint32 lu = cp->ubase + un; UNIT *uptr = rq_getucb (cp, lu); +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_una (Unit=%d)\n", lu); if (uptr == NULL) /* huh? */ return OK; if (!rq_deqf (cp, &pkt)) /* get log pkt */ @@ -2039,9 +2190,8 @@ DEVICE *dptr = rq_devmap[cp->cnum]; if (pkt == 0) /* any packet? */ return OK; -if (DEBUG_PRD (dptr)) - fprintf (sim_deb, ">>RQ%c: rsp=%04X, sts=%04X\n", 'A' + cp->cnum, - cp->pak[pkt].d[RSP_OPF], cp->pak[pkt].d[RSP_STS]); +sim_debug (DBG_REQ, dptr, "rsp=%04X, sts=%04X\n", + cp->pak[pkt].d[RSP_OPF], cp->pak[pkt].d[RSP_STS]); if (!rq_getdesc (cp, &cp->rq, &desc)) /* get rsp desc */ return ERR; if ((desc & UQ_DESC_OWN) == 0) { /* not valid? */ @@ -2146,6 +2296,8 @@ void rq_putr_unit (MSC *cp, int32 pkt, UNIT *uptr, uint32 lu, t_bool all) uint32 dtyp = GET_DTYPE (uptr->flags); /* get drive type */ uint32 maxlbn = (uint32) (uptr->capac / RQ_NUMBY); /* get max lbn */ +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_putr_unit\n"); + cp->pak[pkt].d[ONL_MLUN] = lu; /* unit */ cp->pak[pkt].d[ONL_UFL] = uptr->uf | UF_RPL | RQ_WPH (uptr) | RQ_RMV (uptr); cp->pak[pkt].d[ONL_RSVL] = 0; /* reserved */ @@ -2205,6 +2357,8 @@ return; void rq_setint (MSC *cp) { +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_setint\n"); + cp->irq = 1; /* set ctrl int */ SET_INT (RQ); /* set master int */ return; @@ -2217,6 +2371,8 @@ void rq_clrint (MSC *cp) int32 i; MSC *ncp; +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_clrint\n"); + cp->irq = 0; /* clr ctrl int */ for (i = 0; i < RQ_NUMCT; i++) { /* loop thru ctrls */ ncp = rq_ctxmap[i]; /* get context */ @@ -2256,8 +2412,9 @@ t_bool rq_fatal (MSC *cp, uint32 err) { DEVICE *dptr = rq_devmap[cp->cnum]; -if (DEBUG_PRD (dptr)) - fprintf (sim_deb, ">>RQ%c: fatal err=%X\n", 'A' + cp->cnum, err); +sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_fatal\n"); + +sim_debug (DBG_REQ, dptr, "fatal err=%X\n", err); rq_reset (rq_devmap[cp->cnum]); /* reset device */ cp->sa = SA_ER | err; /* SA = dead code */ cp->csta = CST_DEAD; /* state = dead */ @@ -2330,10 +2487,11 @@ t_stat rq_attach (UNIT *uptr, char *cptr) MSC *cp = rq_ctxmap[uptr->cnum]; t_stat r; -r = attach_unit (uptr, cptr); +r = sim_disk_attach (uptr, cptr, RQ_NUMBY, sizeof (uint16), (uptr->flags & UNIT_NOAUTO), DBG_DSK, drv_tab[GET_DTYPE (uptr->flags)].name, 0); if (r != SCPE_OK) return r; -if (cp->csta == CST_UP) + +if ((cp->csta == CST_UP) && sim_disk_isavailable (uptr)) uptr->flags = uptr->flags | UNIT_ATP; return SCPE_OK; } @@ -2344,7 +2502,7 @@ t_stat rq_detach (UNIT *uptr) { t_stat r; -r = detach_unit (uptr); /* detach unit */ +r = sim_disk_detach (uptr); /* detach unit */ if (r != SCPE_OK) return r; uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP); /* clr onl, atn pend */ @@ -2361,6 +2519,8 @@ UNIT *uptr; MSC *cp; DIB *dibp = (DIB *) dptr->ctxt; +sim_debug (DBG_TRC, dptr, "rq_reset\n"); + for (i = 0, cidx = -1; i < RQ_NUMCT; i++) { /* find ctrl num */ if (rq_devmap[i] == dptr) cidx = i; @@ -2408,11 +2568,10 @@ for (i = 0; i < (RQ_NUMDR + 2); i++) { /* init units */ uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP); uptr->uf = 0; /* clr unit flags */ uptr->cpkt = uptr->pktq = 0; /* clr pkt q's */ + uptr->rqxb = (uint16 *) realloc (uptr->rqxb, (RQ_MAXFR >> 1) * sizeof (uint16)); + if (uptr->rqxb == NULL) + return SCPE_MEM; } -if (rqxb == NULL) - rqxb = (uint16 *) calloc (RQ_MAXFR >> 1, sizeof (uint16)); -if (rqxb == NULL) - return SCPE_MEM; return auto_config (0, 0); /* run autoconfig */ } diff --git a/PDP11/pdp11_tq.c b/PDP11/pdp11_tq.c index 7479f513..fc97d250 100644 --- a/PDP11/pdp11_tq.c +++ b/PDP11/pdp11_tq.c @@ -25,6 +25,23 @@ tq TQK50 tape controller + 05-Mar-11 MP Added missing state for proper save/restore + 01-Mar-11 MP - Migrated complex physical tape activities to sim_tape + - adopted use of asynch I/O interfaces from sim_tape + - Added differing detailed debug output via sim_debug + 14-Jan-11 MP Various fixes discovered while exploring Ultrix issue: + - Set UNIT_SXC flag when a tape mark is encountered + during forward motion read operations. + - Fixed logic which clears UNIT_SXC to check command + modifier. + - Added CMF_WR flag to tq_cmf entry for OP_WTM. + - Made Non-immediate rewind positioning operations + take 2 seconds. + - Added UNIT_IDLE flag to tq units. + - Fixed debug output of tape file positions when they + are 64b. Added more debug output after positioning + operations. Also, added textual display of the + command being performed (GUS,POS,RD,WR,etc…) 18-Jun-07 RMS Added UNIT_IDLE flag to timer thread 16-Feb-06 RMS Revised for new magtape capacity checking 31-Oct-05 RMS Fixed address width for large files @@ -101,7 +118,10 @@ extern int32 cpu_opt; #define pktq u4 /* packet queue */ #define uf buf /* settable unit flags */ #define objp wait /* object position */ +#define io_status u5 /* io status from callback */ +#define io_complete u6 /* io completion flag */ #define TQ_WPH(u) ((sim_tape_wrp (u))? UF_WPH: 0) +#define results up7 /* xfer buffer & results */ #define CST_S1 0 /* init stage 1 */ #define CST_S1_WR 1 /* stage 1 wrap */ @@ -223,11 +243,9 @@ static struct drvtyp drv_tab[] = { extern int32 int_req[IPL_HLVL]; extern int32 tmr_poll, clk_tps; -extern UNIT cpu_unit; extern FILE *sim_deb; extern uint32 sim_taddr_64; -uint8 *tqxb = NULL; /* xfer buffer */ uint32 tq_sa = 0; /* status, addr */ uint32 tq_saw = 0; /* written data */ uint32 tq_s1dat = 0; /* S1 data */ @@ -249,6 +267,7 @@ int32 tq_itime = 200; /* init time, except */ int32 tq_itime4 = 10; /* stage 4 */ int32 tq_qtime = 200; /* queue time */ int32 tq_xtime = 500; /* transfer time */ +int32 tq_rwtime = 2000000; /* rewind time 2 sec (adjusted later) */ int32 tq_typ = INIT_TYPE; /* device type */ /* Command table - legal modifiers (low 16b) and flags (high 16b) */ @@ -280,7 +299,7 @@ static uint32 tq_cmf[64] = { CMF_SEQ|CMF_RW|CMF_WR|MD_CDL|MD_CSE|MD_IMM| /* write */ MD_CMP|MD_ERW|MD_SEC|MD_SER, 0, /* 35 */ - CMF_SEQ|MD_CDL|MD_CSE|MD_IMM, /* wr tape mark */ + CMF_SEQ|CMF_WR|MD_CDL|MD_CSE|MD_IMM, /* wr tape mark */ CMF_SEQ|MD_CDL|MD_CSE|MD_IMM|MD_OBC| /* reposition */ MD_REV|MD_RWD|MD_DLE| MD_SCH|MD_SEC|MD_SER, @@ -289,13 +308,44 @@ static uint32 tq_cmf[64] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +static char *tq_cmdname[] = { + "", /* 0 */ + "ABO", /* 1 b: abort */ + "GCS", /* 2 b: get command status */ + "GUS", /* 3 b: get unit status */ + "SCC", /* 4 b: set controller char */ + "","","", /* 5-7 */ + "AVL", /* 8 b: available */ + "ONL", /* 9 b: online */ + "SUC", /* 10 b: set unit char */ + "DAP", /* 11 b: det acc paths - nop */ + "","","","", /* 12-15 */ + "ACC", /* 16 b: access */ + "CCD", /* 17 d: compare - nop */ + "ERS", /* 18 b: erase */ + "FLU", /* 19 d: flush - nop */ + "","", /* 20-21 */ + "ERG", /* 22 t: erase gap */ + "","","","","","","","","", /* 23-31 */ + "CMP", /* 32 b: compare */ + "RD", /* 33 b: read */ + "WR", /* 34 b: write */ + "", /* 35 */ + "WTM", /* 36 t: write tape mark */ + "POS", /* 37 t: reposition */ + "","","","","","","","","", /* 38-46 */ + "FMT", /* 47 d: format */ + "","","","","","","","","","","","","","","","", /* 48-63 */ + "AVA", /* 64 b: unit now avail */ + }; + /* Forward references */ DEVICE tq_dev; t_stat tq_rd (int32 *data, int32 PA, int32 access); t_stat tq_wr (int32 data, int32 PA, int32 access); -t_stat tq_inta (void); +int32 tq_inta (void); t_stat tq_svc (UNIT *uptr); t_stat tq_tmrsvc (UNIT *uptr); t_stat tq_quesvc (UNIT *uptr); @@ -329,12 +379,10 @@ t_bool tq_dte (UNIT *uptr, uint32 err); t_bool tq_hbe (UNIT *uptr, uint32 ba); t_bool tq_una (UNIT *uptr); uint32 tq_map_status (UNIT *uptr, t_stat st); -uint32 tq_spacef (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec); -uint32 tq_skipff (UNIT *uptr, uint32 cnt, uint32 *skipped); -uint32 tq_rdbuff (UNIT *uptr, t_mtrlnt *tbc); -uint32 tq_spacer (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec); -uint32 tq_skipfr (UNIT *uptr, uint32 cnt, uint32 *skipped); -uint32 tq_rdbufr (UNIT *uptr, t_mtrlnt *tbc); +void tq_rdbuff_top (UNIT *uptr, t_mtrlnt *tbc); +uint32 tq_rdbuff_bottom (UNIT *uptr, t_mtrlnt *tbc); +void tq_rdbufr_top (UNIT *uptr, t_mtrlnt *tbc); +uint32 tq_rdbufr_bottom (UNIT *uptr, t_mtrlnt *tbc); t_bool tq_deqf (int32 *pkt); int32 tq_deqh (int32 *lh); void tq_enqh (int32 *lh, int32 pkt); @@ -369,12 +417,12 @@ DIB tq_dib = { }; UNIT tq_unit[] = { - { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, - { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, - { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, - { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, - { UDATA (&tq_tmrsvc, UNIT_DIS, 0) }, - { UDATA (&tq_quesvc, UNIT_DIS, 0) } + { UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, + { UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, + { UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, + { UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, + { UDATA (&tq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0) }, + { UDATA (&tq_quesvc, UNIT_IDLE|UNIT_DIS, 0) } }; #define TQ_TIMER (TQ_NUMDR) @@ -384,9 +432,11 @@ REG tq_reg[] = { { GRDATA (SA, tq_sa, DEV_RDX, 16, 0) }, { GRDATA (SAW, tq_saw, DEV_RDX, 16, 0) }, { GRDATA (S1DAT, tq_s1dat, DEV_RDX, 16, 0) }, + { GRDATA (CQIOFF, tq_cq.ioff, DEV_RDX, 32, 0) }, { GRDATA (CQBA, tq_cq.ba, DEV_RDX, 22, 0) }, { GRDATA (CQLNT, tq_cq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (CQIDX, tq_cq.idx, DEV_RDX, 8, 2) }, + { GRDATA (TQIOFF, tq_rq.ioff, DEV_RDX, 32, 0) }, { GRDATA (TQBA, tq_rq.ba, DEV_RDX, 22, 0) }, { GRDATA (TQLNT, tq_rq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (TQIDX, tq_rq.idx, DEV_RDX, 8, 2) }, @@ -411,6 +461,7 @@ REG tq_reg[] = { { DRDATA (I4TIME, tq_itime4, 24), PV_LEFT + REG_NZ }, { DRDATA (QTIME, tq_qtime, 24), PV_LEFT + REG_NZ }, { DRDATA (XTIME, tq_xtime, 24), PV_LEFT + REG_NZ }, + { DRDATA (RWTIME, tq_rwtime, 32), PV_LEFT + REG_NZ }, { BRDATA (PKTS, tq_pkt, DEV_RDX, 16, TQ_NPKTS * (TQ_PKT_SIZE_W + 1)) }, { DRDATA (DEVTYPE, tq_typ, 2), REG_HRO }, { DRDATA (DEVCAP, drv_tab[TQU_TYPE].cap, T_ADDR_W), PV_LEFT | REG_HRO }, @@ -458,12 +509,44 @@ MTAB tq_mod[] = { { 0 } }; +/* debugging bitmaps */ +#define DBG_TRC 0x0001 /* trace routine calls */ +#define DBG_INI 0x0002 /* display setup/init sequence info */ +#define DBG_REG 0x0004 /* trace read/write registers */ +#define DBG_REQ 0x0008 /* display transfer requests */ +#define DBG_TAP 0x0010 /* display sim_tape activities */ +#define DBG_DAT 0x0020 /* display transfer data */ + +DEBTAB tq_debug[] = { + {"TRACE", DBG_TRC}, + {"INIT", DBG_INI}, + {"REG", DBG_REG}, + {"REQ", DBG_REQ}, + {"TAPE", DBG_TAP}, + {"DATA", DBG_DAT}, + {0} +}; + DEVICE tq_dev = { "TQ", tq_unit, tq_reg, tq_mod, TQ_NUMDR + 2, 10, T_ADDR_W, 1, DEV_RDX, 8, NULL, NULL, &tq_reset, &tq_boot, &tq_attach, &tq_detach, - &tq_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG + &tq_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG, + 0, tq_debug + }; + + +struct tq_req_results { /* intermediate State during tape motion commands */ + t_stat io_status; + int32 io_complete; + int rewind_done; + uint32 sts; + uint32 sktmk; + uint32 skrec; + t_mtrlnt tbc; + int32 objupd; + uint8 tqxb[TQ_MAXFR]; }; /* I/O dispatch routines, I/O addresses 17772150 - 17772152 @@ -474,6 +557,8 @@ DEVICE tq_dev = { t_stat tq_rd (int32 *data, int32 PA, int32 access) { +sim_debug(DBG_REG, &tq_dev, "tq_rd(PA=0x%08X [%s], access=%d)\n", PA, ((PA >> 1) & 01) ? "IP" : "SA", access); + switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* IP */ *data = 0; /* reads zero */ @@ -495,13 +580,13 @@ return SCPE_OK; t_stat tq_wr (int32 data, int32 PA, int32 access) { +sim_debug(DBG_REG, &tq_dev, "tq_wr(PA=0x%08X [%s], access=%d)\n", PA, ((PA >> 1) & 01) ? "IP" : "SA", access); + switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* IP */ tq_reset (&tq_dev); /* init device */ - if (DEBUG_PRS (tq_dev)) - fprintf (sim_deb, ">>TQ: initialization started, time=%.0f\n", - sim_gtime ()); + sim_debug (DBG_REQ, &tq_dev, "initialization started\n"); break; case 1: /* SA */ @@ -565,9 +650,12 @@ int32 i, cnid; int32 pkt = 0; UNIT *nuptr; -if (tq_csta < CST_UP) { /* still init? */ - switch (tq_csta) { /* controller state? */ +sim_debug(DBG_TRC, &tq_dev, "tq_quesvc\n"); +if (tq_csta < CST_UP) { /* still init? */ + sim_debug(DBG_INI, &tq_dev, "CSTA=%d, SAW=0x%X\n", tq_csta, tq_saw); + + switch (tq_csta) { /* controller state? */ case CST_S1: /* need S1 reply */ if (tq_saw & SA_S1H_VL) { /* valid? */ if (tq_saw & SA_S1H_WR) { /* wrap? */ @@ -615,8 +703,7 @@ if (tq_csta < CST_UP) { /* still init? */ case CST_S4: /* need S4 reply */ if (tq_saw & SA_S4H_GO) { /* go set? */ - if (DEBUG_PRS (tq_dev)) - fprintf (sim_deb, ">>TQ: initialization complete\n"); + sim_debug (DBG_REQ, &tq_dev, "initialization complete\n"); tq_csta = CST_UP; /* we're up */ tq_sa = 0; /* clear SA */ sim_activate (&tq_unit[TQ_TIMER], tmr_poll * clk_tps); @@ -641,18 +728,21 @@ if ((pkt == 0) && tq_pip) { /* polling? */ if (!tq_getpkt (&pkt)) /* get host pkt */ return SCPE_OK; if (pkt) { /* got one? */ - if (DEBUG_PRS (tq_dev)) { - UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]); - fprintf (sim_deb, ">>TQ: cmd=%04X, mod=%04X, unit=%d, ", - tq_pkt[pkt].d[CMD_OPC], tq_pkt[pkt].d[CMD_MOD], tq_pkt[pkt].d[CMD_UN]); - fprintf (sim_deb, "bc=%04X%04X, ma=%04X%04X", - tq_pkt[pkt].d[RW_BCH], tq_pkt[pkt].d[RW_BCL], - tq_pkt[pkt].d[RW_BAH], tq_pkt[pkt].d[RW_BAL]); - if (up) - fprintf (sim_deb, ", pos=%d, obj=%d\n", up->pos, up->objp); - else fprintf (sim_deb, "\n"); - fflush (sim_deb); - } + UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]); + + if (up) + sim_debug (DBG_REQ, &tq_dev, "cmd=%04X(%3s), mod=%04X, unit=%d, bc=%04X%04X, ma=%04X%04X, obj=%d, pos=0x%X\n", + tq_pkt[pkt].d[CMD_OPC], tq_cmdname[tq_pkt[pkt].d[CMD_OPC]&0x3f], + tq_pkt[pkt].d[CMD_MOD], tq_pkt[pkt].d[CMD_UN], + tq_pkt[pkt].d[RW_BCH], tq_pkt[pkt].d[RW_BCL], + tq_pkt[pkt].d[RW_BAH], tq_pkt[pkt].d[RW_BAL], + up->objp, up->pos); + else + sim_debug (DBG_REQ, &tq_dev, "cmd=%04X(%3s), mod=%04X, unit=%d, bc=%04X%04X, ma=%04X%04X\n", + tq_pkt[pkt].d[CMD_OPC], tq_cmdname[tq_pkt[pkt].d[CMD_OPC]&0x3f], + tq_pkt[pkt].d[CMD_MOD], tq_pkt[pkt].d[CMD_UN], + tq_pkt[pkt].d[RW_BCH], tq_pkt[pkt].d[RW_BCL], + tq_pkt[pkt].d[RW_BAH], tq_pkt[pkt].d[RW_BAL]); if (GETP (pkt, UQ_HCTC, TYP) != UQ_TYP_SEQ) /* seq packet? */ return tq_fatal (PE_PIE); /* no, term thread */ cnid = GETP (pkt, UQ_HCTC, CID); /* get conn ID */ @@ -686,6 +776,8 @@ t_stat tq_tmrsvc (UNIT *uptr) int32 i; UNIT *nuptr; +sim_debug(DBG_TRC, &tq_dev, "tq_tmrsvc\n"); + sim_activate (uptr, tmr_poll * clk_tps); /* reactivate */ for (i = 0; i < TQ_NUMDR; i++) { /* poll */ nuptr = tq_dev.units + i; @@ -713,6 +805,8 @@ uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifier */ uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ UNIT *uptr; +sim_debug(DBG_TRC, &tq_dev, "tq_mscp\n"); + if ((cmd >= 64) || (tq_cmf[cmd] == 0)) { /* invalid cmd? */ cmd = OP_END; /* set end op */ sts = ST_CMD | I_OPCD; /* ill op */ @@ -734,8 +828,9 @@ else { /* valid cmd */ } /* if (tq_cmf[cmd] & MD_CDL) /* clr cch lost? */ /* uptr->flags = uptr->flags & ~UNIT_CDL; */ - if (tq_cmf[cmd] & MD_CSE) /* clr ser exc? */ + if ((mdf & MD_CSE) && (uptr->flags & UNIT_SXC)) /* clr ser exc? */ uptr->flags = uptr->flags & ~UNIT_SXC; + memset (uptr->results, 0, sizeof (struct tq_req_results)); /* init request state */ } switch (cmd) { @@ -803,6 +898,8 @@ uint32 ref = GETP32 (pkt, ABO_REFL); /* cmd ref # */ int32 tpkt, prv; UNIT *uptr; +sim_debug(DBG_TRC, &tq_dev, "tq_abo\n"); + tpkt = 0; /* set no mtch */ if (uptr = tq_getucb (lu)) { /* get unit */ if (uptr->cpkt && /* curr pkt? */ @@ -846,20 +943,22 @@ uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifiers */ uint32 sts; UNIT *uptr; +sim_debug(DBG_TRC, &tq_dev, "tq_avl\n"); + if (uptr = tq_getucb (lu)) { /* unit exist? */ - if (uptr->flags & UNIT_SXC) /* ser exc pending? */ + if (uptr->flags & UNIT_SXC) /* ser exc pending? */ sts = ST_SXC; - else { - uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_TMK | UNIT_POL); - sim_tape_rewind (uptr); /* rewind */ - uptr->uf = uptr->objp = 0; /* clr flags */ - if (uptr->flags & UNIT_ATT) { /* attached? */ - sts = ST_SUC; /* success */ - if (mdf & MD_UNL) /* unload? */ + else { + uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_TMK | UNIT_POL); + sim_tape_rewind (uptr); /* rewind */ + uptr->uf = uptr->objp = 0; /* clr flags */ + if (uptr->flags & UNIT_ATT) { /* attached? */ + sts = ST_SUC; /* success */ + if (mdf & MD_UNL) /* unload? */ tq_detach (uptr); - } - else sts = ST_OFL | SB_OFL_NV; /* no, offline */ - } + } + else sts = ST_OFL | SB_OFL_NV; /* no, offline */ + } } else sts = ST_OFL; /* offline */ tq_putr (pkt, OP_AVL | OP_END, tq_efl (uptr), sts, AVL_LNT, UQ_TYP_SEQ); @@ -875,6 +974,8 @@ uint32 ref = GETP32 (pkt, GCS_REFL); /* ref # */ int32 tpkt; UNIT *uptr; +sim_debug(DBG_TRC, &tq_dev, "tq_gcs\n"); + if ((uptr = tq_getucb (lu)) && /* valid lu? */ (tpkt = uptr->cpkt) && /* queued pkt? */ (GETP32 (tpkt, CMD_REFL) == ref) && /* match ref? */ @@ -895,6 +996,8 @@ uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 sts; UNIT *uptr; +sim_debug(DBG_TRC, &tq_dev, "tq_gus\n"); + if (tq_pkt[pkt].d[CMD_MOD] & MD_NXU) { /* next unit? */ if (lu >= TQ_NUMDR) { /* end of range? */ lu = 0; /* reset to 0 */ @@ -926,6 +1029,8 @@ uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 sts; UNIT *uptr; +sim_debug(DBG_TRC, &tq_dev, "tq_onl\n"); + if (uptr = tq_getucb (lu)) { /* unit exist? */ if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ sts = ST_OFL | SB_OFL_NV; /* offl no vol */ @@ -950,6 +1055,8 @@ return tq_putpkt (pkt, TRUE); t_bool tq_scc (int32 pkt) { +sim_debug(DBG_TRC, &tq_dev, "tq_scc\n"); + if (tq_pkt[pkt].d[SCC_MSV]) /* MSCP ver = 0? */ tq_putr (pkt, 0, 0, ST_CMD | I_VRSN, SCC_LNT, UQ_TYP_SEQ); else { @@ -979,6 +1086,8 @@ uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 sts; UNIT *uptr; +sim_debug(DBG_TRC, &tq_dev, "tq_suc\n"); + if (uptr = tq_getucb (lu)) { /* unit exist? */ if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ sts = ST_OFL | SB_OFL_NV; /* offl no vol */ @@ -1001,6 +1110,8 @@ uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 sts; UNIT *uptr; +sim_debug(DBG_TRC, &tq_dev, "tq_flu\n"); + if (uptr = tq_getucb (lu)) /* unit exist? */ sts = tq_mot_valid (uptr, OP_FLU); /* validate req */ else sts = ST_OFL; /* offline */ @@ -1017,11 +1128,14 @@ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 sts; UNIT *uptr; +sim_debug(DBG_TRC, &tq_dev, "tq_erase\n"); + if (uptr = tq_getucb (lu)) { /* unit exist? */ sts = tq_mot_valid (uptr, cmd); /* validity checks */ if (sts == ST_SUC) { /* ok? */ uptr->cpkt = pkt; /* op in progress */ - sim_activate (uptr, tq_xtime); /* activate */ + uptr->iostarttime = sim_grtime(); + sim_activate (uptr, 0); /* activate */ return OK; /* done */ } } @@ -1038,12 +1152,15 @@ uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 sts, objp = 0; UNIT *uptr; +sim_debug(DBG_TRC, &tq_dev, "tq_wtm\n"); + if (uptr = tq_getucb (lu)) { /* unit exist? */ objp = uptr->objp; /* position op */ sts = tq_mot_valid (uptr, OP_WTM); /* validity checks */ if (sts == ST_SUC) { /* ok? */ uptr->cpkt = pkt; /* op in progress */ - sim_activate (uptr, tq_xtime); /* activate */ + uptr->iostarttime = sim_grtime(); + sim_activate (uptr, 0); /* activate */ return OK; /* done */ } } @@ -1061,12 +1178,21 @@ uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 sts, objp = 0; UNIT *uptr; +sim_debug(DBG_TRC, &tq_dev, "tq_pos\n"); + if (uptr = tq_getucb (lu)) { /* unit exist? */ objp = uptr->objp; /* position op */ sts = tq_mot_valid (uptr, OP_POS); /* validity checks */ if (sts == ST_SUC) { /* ok? */ uptr->cpkt = pkt; /* op in progress */ - sim_activate (uptr, tq_xtime); /* activate */ + tq_rwtime = 2 * tmr_poll * clk_tps; /* 2 second rewind time */ + if ((tq_pkt[pkt].d[CMD_MOD] & MD_RWD) && /* rewind? */ + (!(tq_pkt[pkt].d[CMD_MOD] & MD_IMM))) /* !immediate? */ + sim_activate (uptr, tq_rwtime); /* use 2 sec rewind execute time */ + else { /* otherwise */ + uptr->iostarttime = sim_grtime(); + sim_activate (uptr, 0); /* use normal execute time */ + } return OK; /* done */ } } @@ -1088,6 +1214,8 @@ uint32 bc = GETP32 (pkt, RW_BCL); /* byte count */ uint32 sts, objp = 0; UNIT *uptr; +sim_debug(DBG_TRC, &tq_dev, "tq_rw\n"); + if (uptr = tq_getucb (lu)) { /* unit exist? */ objp = uptr->objp; /* position op */ sts = tq_mot_valid (uptr, cmd); /* validity checks */ @@ -1098,7 +1226,8 @@ if (uptr = tq_getucb (lu)) { /* unit exist? */ } else { uptr->cpkt = pkt; /* op in progress */ - sim_activate (uptr, tq_xtime); /* activate */ + uptr->iostarttime = sim_grtime(); + sim_activate (uptr, 0); /* activate */ return OK; /* done */ } } @@ -1115,6 +1244,8 @@ return tq_putpkt (pkt, TRUE); int32 tq_mot_valid (UNIT *uptr, uint32 cmd) { +sim_debug(DBG_TRC, &tq_dev, "tq_mot_valid\n"); + if (uptr->flags & UNIT_SXC) /* ser exc pend? */ return ST_SXC; if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ @@ -1136,10 +1267,28 @@ return ST_SUC; /* success! */ /* Unit service for motion commands */ +/* I/O completion callback */ + +void tq_io_complete (UNIT *uptr, t_stat status) +{ +struct tq_req_results *res = (struct tq_req_results *)uptr->results; +int32 elapsed = sim_grtime()-uptr->iostarttime; + +sim_debug(DBG_TRC, &tq_dev, "tq_io_complete(status=%d)\n", status); + +res->io_status = status; +res->io_complete = 1; +if (elapsed > tq_xtime) + sim_activate (uptr, 0); +else + sim_activate (uptr, tq_xtime-elapsed); +} + + t_stat tq_svc (UNIT *uptr) { -uint32 t, sts, sktmk, skrec; -t_mtrlnt i, tbc, wbc; +uint32 t; +t_mtrlnt wbc; int32 pkt = uptr->cpkt; /* get packet */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifier */ @@ -1147,7 +1296,14 @@ uint32 ba = GETP32 (pkt, RW_BAL); /* buf addr */ t_mtrlnt bc = GETP32 (pkt, RW_BCL); /* byte count */ uint32 nrec = GETP32 (pkt, POS_RCL); /* #rec to skip */ uint32 ntmk = GETP32 (pkt, POS_TMCL); /* #tmk to skp */ +struct tq_req_results *res = (struct tq_req_results *)uptr->results; +int32 io_complete = res->io_complete; +sim_debug (DBG_TRC, &tq_dev, "tq_svc(unit=%d, pkt=%d, cmd=%s, mdf=0x%0X, bc=0x%0x, phase=%s)\n", + uptr-tq_dev.units, pkt, tq_cmdname[tq_pkt[pkt].d[CMD_OPC]&0x3f], mdf, bc, + uptr->io_complete ? "bottom" : "top"); + +res->io_complete = 0; if (pkt == 0) /* what??? */ return SCPE_IERR; if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ @@ -1167,58 +1323,71 @@ if (tq_cmf[cmd] & CMF_WR) { /* write op? */ return SCPE_OK; } } -sts = ST_SUC; /* assume success */ -tbc = 0; /* assume zero rec */ +if (!io_complete) { + res->sts = ST_SUC; /* assume success */ + res->tbc = 0; /* assume zero rec */ + } switch (cmd) { /* case on command */ case OP_RD:case OP_ACC:case OP_CMP: /* read-like op */ - if (mdf & MD_REV) /* read record */ - sts = tq_rdbufr (uptr, &tbc); - else sts = tq_rdbuff (uptr, &tbc); - if (sts == ST_DRV) { /* read error? */ - PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */ - return tq_mot_err (uptr, tbc); /* log, done */ + if (!io_complete) { + if (mdf & MD_REV) /* read record */ + tq_rdbufr_top (uptr, &res->tbc); + else + tq_rdbuff_top (uptr, &res->tbc); + return SCPE_OK; } - if ((sts != ST_SUC) || (cmd == OP_ACC)) { /* error or access? */ + if (mdf & MD_REV) /* read record */ + res->sts = tq_rdbufr_bottom (uptr, &res->tbc); + else + res->sts = tq_rdbuff_bottom (uptr, &res->tbc); + if (res->sts == ST_DRV) { /* read error? */ + PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */ + return tq_mot_err (uptr, res->tbc); /* log, done */ + } + if ((res->sts != ST_SUC) || (cmd == OP_ACC)) { /* error or access? */ + if (res->sts == ST_TMK) + uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */ break; } - if (tbc > bc) { /* tape rec > buf? */ + if (res->tbc > bc) { /* tape rec > buf? */ uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */ - sts = ST_RDT; /* data truncated */ + res->sts = ST_RDT; /* data truncated */ wbc = bc; /* set working bc */ } - else wbc = tbc; + else wbc = res->tbc; if (cmd == OP_RD) { /* read? */ - if (t = Map_WriteB (ba, wbc, tqxb)) { /* store, nxm? */ + if (t = Map_WriteB (ba, wbc, res->tqxb)) { /* store, nxm? */ PUTP32 (pkt, RW_BCL, wbc - t); /* adj bc */ if (tq_hbe (uptr, ba + wbc - t)) /* post err log */ - tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, tbc); + tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, res->tbc); return SCPE_OK; /* end if nxm */ } } /* end if read */ else { /* compare */ uint8 mby, dby; uint32 mba; + t_mtrlnt i; for (i = 0; i < wbc; i++) { /* loop */ if (mdf & MD_REV) { /* reverse? */ mba = ba + bc - 1 - i; /* mem addr */ - dby = tqxb[tbc - 1 - i]; /* byte */ + dby = ((uint8 *)res->tqxb)[res->tbc - 1 - i]; /* byte */ } else { mba = ba + i; - dby = tqxb[i]; + dby = ((uint8 *)res->tqxb)[i]; } if (Map_ReadB (mba, 1, &mby)) { /* fetch, nxm? */ PUTP32 (pkt, RW_BCL, i); /* adj bc */ if (tq_hbe (uptr, mba)) /* post err log */ - tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, tbc); + tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, res->tbc); return SCPE_OK; } if (mby != dby) { /* cmp err? */ uptr->flags = uptr->flags | UNIT_SXC; /* ser exc */ PUTP32 (pkt, RW_BCL, i); /* adj bc */ - tq_mot_end (uptr, 0, ST_CMP, tbc); + tq_mot_end (uptr, 0, ST_CMP, res->tbc); return SCPE_OK; /* exit */ } } /* end for */ @@ -1227,23 +1396,31 @@ switch (cmd) { /* case on command */ break; case OP_WR: /* write */ - if (t = Map_ReadB (ba, bc, tqxb)) { /* fetch buf, nxm? */ - PUTP32 (pkt, RW_BCL, 0); /* no bytes xfer'd */ - if (tq_hbe (uptr, ba + bc - t)) /* post err log */ - tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, bc); - return SCPE_OK; /* end else wr */ - } - if (sim_tape_wrrecf (uptr, tqxb, bc)) /* write rec fwd, err? */ + if (!io_complete) { /* Top half processing */ + if (t = Map_ReadB (ba, bc, res->tqxb)) { /* fetch buf, nxm? */ + PUTP32 (pkt, RW_BCL, 0); /* no bytes xfer'd */ + if (tq_hbe (uptr, ba + bc - t)) /* post err log */ + tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, bc); + return SCPE_OK; /* end else wr */ + } + sim_tape_wrrecf_a (uptr, res->tqxb, bc, tq_io_complete); /* write rec fwd */ + return SCPE_OK; + } + if (res->io_status) return tq_mot_err (uptr, bc); /* log, end */ uptr->objp = uptr->objp + 1; /* upd obj pos */ if (TEST_EOT (uptr)) /* EOT on write? */ uptr->flags = uptr->flags | UNIT_SXC; uptr->flags = uptr->flags & ~UNIT_TMK; /* disable LEOT */ - tbc = bc; /* RW_BC is ok */ + res->tbc = bc; /* RW_BC is ok */ break; case OP_WTM: /* write tape mark */ - if (sim_tape_wrtmk (uptr)) /* write tmk, err? */ + if (!io_complete) { /* Top half processing */ + sim_tape_wrtmk_a (uptr, tq_io_complete); /* write tmk, err? */ + return SCPE_OK; + } + if (res->io_status) return tq_mot_err (uptr, 0); /* log, end */ uptr->objp = uptr->objp + 1; /* incr obj cnt */ case OP_ERG: /* erase gap */ @@ -1253,46 +1430,47 @@ switch (cmd) { /* case on command */ break; case OP_ERS: /* erase */ - if (sim_tape_wreom (uptr)) /* write eom, err? */ + if (!io_complete) { /* Top half processing */ + sim_tape_wreomrw_a (uptr, tq_io_complete); /* write eom, err? */ + return SCPE_OK; + } + if (res->io_status) return tq_mot_err (uptr, 0); /* log, end */ - sim_tape_rewind (uptr); /* rewind */ uptr->objp = 0; uptr->flags = uptr->flags & ~(UNIT_TMK | UNIT_POL); break; case OP_POS: /* position */ - sktmk = skrec = 0; /* clr skipped */ - if (mdf & MD_RWD) { /* rewind? */ - sim_tape_rewind (uptr); - uptr->objp = 0; /* clr flags */ - uptr->flags = uptr->flags & ~(UNIT_TMK | UNIT_POL); - } - if (mdf & MD_OBC) { /* skip obj? */ - if (mdf & MD_REV) /* reverse? */ - sts = tq_spacer (uptr, nrec, &skrec, FALSE); - else sts = tq_spacef (uptr, nrec, &skrec, FALSE); - } - else { /* skip tmk, rec */ - if (mdf & MD_REV) - sts = tq_skipfr (uptr, ntmk, &sktmk); - else sts = tq_skipff (uptr, ntmk, &sktmk); - if (sts == ST_SUC) { /* tmk succeed? */ - if (mdf & MD_REV) /* reverse? */ - sts = tq_spacer (uptr, nrec, &skrec, TRUE); - else sts = tq_spacef (uptr, nrec, &skrec, TRUE); - if (sts == ST_TMK) - sktmk = sktmk + 1; + if (!io_complete) { /* Top half processing */ + res->sktmk = res->skrec = 0; /* clr skipped */ + if (mdf & MD_RWD) { /* rewind? */ + uptr->objp = 0; /* clr flags */ + uptr->flags = uptr->flags & ~(UNIT_TMK | UNIT_POL); } + sim_tape_position_a (uptr, + ((mdf & MD_RWD) ? MTPOS_M_REW : 0) | + ((mdf & MD_REV) ? MTPOS_M_REV : 0) | + ((mdf & MD_OBC) ? MTPOS_M_OBJ : 0) , + nrec, &res->skrec, ntmk, &res->sktmk, (uint32 *)&res->objupd, tq_io_complete); + return SCPE_OK; } - PUTP32 (pkt, POS_RCL, skrec); /* #rec skipped */ - PUTP32 (pkt, POS_TMCL, sktmk); /* #tmk skipped */ + if (res->io_status) + return tq_mot_err (uptr, 0); /* log, end */ + sim_debug (DBG_REQ, &tq_dev, "Position Done: mdf=0x%04X, nrec=%d, ntmk=%d, skrec=%d, sktmk=%d, skobj=%d\n", + mdf, nrec, ntmk, res->skrec, res->sktmk, res->objupd); + if (mdf & MD_REV) + uptr->objp = uptr->objp - res->objupd; + else + uptr->objp = uptr->objp + res->objupd; + PUTP32 (pkt, POS_RCL, res->skrec); /* #rec skipped */ + PUTP32 (pkt, POS_TMCL, res->sktmk); /* #tmk skipped */ break; default: return SCPE_IERR; } -tq_mot_end (uptr, 0, sts, tbc); /* done */ +tq_mot_end (uptr, 0, res->sts, res->tbc); /* done */ return SCPE_OK; } @@ -1384,90 +1562,21 @@ switch (st) { return ST_SUC; } -uint32 tq_spacef (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec) -{ -t_stat st; -t_mtrlnt tbc; - -*skipped = 0; -while (*skipped < cnt) { /* loop */ - st = sim_tape_sprecf (uptr, &tbc); /* space rec fwd */ - if ((st != MTSE_OK) && (st != MTSE_TMK)) /* real error? */ - return tq_map_status (uptr, st); /* map status */ - uptr->objp = uptr->objp + 1; /* upd obj cnt */ - if (st == MTSE_TMK) { /* tape mark? */ - int32 pkt = uptr->cpkt; /* get pkt */ - if ((tq_pkt[pkt].d[CMD_MOD] & MD_DLE) && /* LEOT? */ - (uptr->flags & UNIT_TMK)) { - sim_tape_sprecr (uptr, &tbc); /* rev over tmk */ - uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */ - return ST_LED; - } - uptr->flags = uptr->flags | UNIT_TMK; /* set TM seen */ - if (qrec) /* rec spc? stop */ - return ST_TMK; - } - else uptr->flags = uptr->flags & ~UNIT_TMK; /* clr TM seen */ - *skipped = *skipped + 1; /* # obj skipped */ - } -return ST_SUC; -} - -uint32 tq_skipff (UNIT *uptr, uint32 cnt, uint32 *skipped) -{ -uint32 st, skrec; - -*skipped = 0; -while (*skipped < cnt) { /* loop */ - st = tq_spacef (uptr, 0x7FFFFFFF, &skrec, TRUE); /* rec spc fwd */ - if (st == ST_TMK) /* count files */ - *skipped = *skipped + 1; - else if (st != ST_SUC) - return st; - } -return ST_SUC; -} - -uint32 tq_spacer (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec) -{ -t_stat st; -t_mtrlnt tbc; - -*skipped = 0; -while (*skipped < cnt) { /* loop */ - st = sim_tape_sprecr (uptr, &tbc); /* spc rec rev */ - if ((st != MTSE_OK) && (st != MTSE_TMK)) /* real error? */ - return tq_map_status (uptr, st); /* map status */ - uptr->objp = uptr->objp - 1; /* upd obj cnt */ - if ((st == MTSE_TMK) && qrec) /* tape mark, stop? */ - return ST_TMK; - *skipped = *skipped + 1; /* # obj skipped */ - } -return ST_SUC; -} - -uint32 tq_skipfr (UNIT *uptr, uint32 cnt, uint32 *skipped) -{ -uint32 st, skrec; - -*skipped = 0; -while (*skipped < cnt) { /* loopo */ - st = tq_spacer (uptr, 0x7FFFFFFF, &skrec, TRUE); /* rec spc rev */ - if (st == ST_TMK) /* tape mark? */ - *skipped = *skipped + 1; - else if (st != 0) /* error? */ - return st; - } -return ST_SUC; -} - /* Read buffer - can return ST_TMK, ST_FMT, or ST_DRV */ -uint32 tq_rdbuff (UNIT *uptr, t_mtrlnt *tbc) +void tq_rdbuff_top (UNIT *uptr, t_mtrlnt *tbc) +{ +struct tq_req_results *res = (struct tq_req_results *)uptr->results; + +sim_tape_rdrecf_a (uptr, res->tqxb, tbc, MT_MAXFR, tq_io_complete);/* read rec fwd */ +} + +uint32 tq_rdbuff_bottom (UNIT *uptr, t_mtrlnt *tbc) { t_stat st; +struct tq_req_results *res = (struct tq_req_results *)uptr->results; -st = sim_tape_rdrecf (uptr, tqxb, tbc, MT_MAXFR); /* read rec fwd */ +st = res->io_status; /* read rec fwd io status */ if (st == MTSE_TMK) { /* tape mark? */ uptr->flags = uptr->flags | UNIT_SXC | UNIT_TMK; /* serious exc */ uptr->objp = uptr->objp + 1; /* update obj cnt */ @@ -1480,11 +1589,19 @@ uptr->objp = uptr->objp + 1; /* upd obj cnt */ return ST_SUC; } -uint32 tq_rdbufr (UNIT *uptr, t_mtrlnt *tbc) +void tq_rdbufr_top (UNIT *uptr, t_mtrlnt *tbc) +{ +struct tq_req_results *res = (struct tq_req_results *)uptr->results; + +sim_tape_rdrecr_a (uptr, res->tqxb, tbc, MT_MAXFR, tq_io_complete); /* read rec rev */ +} + +uint32 tq_rdbufr_bottom (UNIT *uptr, t_mtrlnt *tbc) { t_stat st; +struct tq_req_results *res = (struct tq_req_results *)uptr->results; -st = sim_tape_rdrecr (uptr, tqxb, tbc, MT_MAXFR); /* read rec rev */ +st = res->io_status; /* read rec rev io status */ if (st == MTSE_TMK) { /* tape mark? */ uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */ uptr->objp = uptr->objp - 1; /* update obj cnt */ @@ -1584,7 +1701,7 @@ return tq_putpkt (pkt, TRUE); /* Unit now available attention packet */ -int32 tq_una (UNIT *uptr) +t_bool tq_una (UNIT *uptr) { int32 pkt; uint32 lu; @@ -1683,18 +1800,17 @@ return tq_putdesc (&tq_cq, desc); /* release desc */ t_bool tq_putpkt (int32 pkt, t_bool qt) { uint32 addr, desc, lnt, cr; +UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]); if (pkt == 0) /* any packet? */ return OK; -if (DEBUG_PRS (tq_dev)) { - UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]); - fprintf (sim_deb, ">>TQ: rsp=%04X, sts=%04X", - tq_pkt[pkt].d[RSP_OPF], tq_pkt[pkt].d[RSP_STS]); - if (up) - fprintf (sim_deb, ", pos=%d, obj=%d\n", up->pos, up->objp); - else fprintf (sim_deb, "\n"); - fflush (sim_deb); - } +if (up) + sim_debug (DBG_REQ, &tq_dev, "rsp=%04X, sts=%04X, rszl=%04X, obj=%d, pos=%d\n", + tq_pkt[pkt].d[RSP_OPF], tq_pkt[pkt].d[RSP_STS], tq_pkt[pkt].d[RW_RSZL], + up->objp, up->pos); +else + sim_debug (DBG_REQ, &tq_dev, "rsp=%04X, sts=%04X\n", + tq_pkt[pkt].d[RSP_OPF], tq_pkt[pkt].d[RSP_STS]); if (!tq_getdesc (&tq_rq, &desc)) /* get rsp desc */ return ERR; if ((desc & UQ_DESC_OWN) == 0) { /* not valid? */ @@ -1816,6 +1932,7 @@ void tq_putr_unit (int32 pkt, UNIT *uptr, uint32 lu, t_bool all) { tq_pkt[pkt].d[ONL_MLUN] = lu; /* multi-unit */ tq_pkt[pkt].d[ONL_UFL] = uptr->uf | TQ_WPH (uptr); /* unit flags */ +tq_pkt[pkt].d[ONL_UFL] |= tq_efl (uptr); /* end flags accordingly */ tq_pkt[pkt].d[ONL_RSVL] = tq_pkt[pkt].d[ONL_RSVH] = 0; /* reserved */ tq_pkt[pkt].d[ONL_UIDA] = lu; /* UID low */ tq_pkt[pkt].d[ONL_UIDB] = 0; @@ -1879,8 +1996,9 @@ return tq_dib.vec; /* prog vector */ t_bool tq_fatal (uint32 err) { -if (DEBUG_PRS (tq_dev)) - fprintf (sim_deb, ">>TQ: fatal err=%X\n", err); +sim_debug (DBG_TRC, &tq_dev, "tq_fatal\n"); + +sim_debug (DBG_REQ, &tq_dev, "fatal err=%X\n", err); tq_reset (&tq_dev); /* reset device */ tq_sa = SA_ER | err; /* SA = dead code */ tq_csta = CST_DEAD; /* state = dead */ @@ -1894,7 +2012,7 @@ t_stat tq_attach (UNIT *uptr, char *cptr) { t_stat r; -r = sim_tape_attach (uptr, cptr); +r = sim_tape_attach_ex (uptr, cptr, DBG_TAP); if (r != SCPE_OK) return r; if (tq_csta == CST_UP) @@ -1955,11 +2073,11 @@ for (i = 0; i < TQ_NUMDR + 2; i++) { /* init units */ ~(UNIT_ONL|UNIT_ATP|UNIT_SXC|UNIT_POL|UNIT_TMK); uptr->uf = 0; /* clr unit flags */ uptr->cpkt = uptr->pktq = 0; /* clr pkt q's */ + if (uptr->results == NULL) + uptr->results = calloc (1, sizeof (struct tq_req_results)); + if (uptr->results == NULL) + return SCPE_MEM; } -if (tqxb == NULL) - tqxb = (uint8 *) calloc (TQ_MAXFR, sizeof (uint8)); -if (tqxb == NULL) - return SCPE_MEM; return SCPE_OK; } diff --git a/VAX/vax780_sbi.c b/VAX/vax780_sbi.c index 7debc2d2..afcb6914 100644 --- a/VAX/vax780_sbi.c +++ b/VAX/vax780_sbi.c @@ -27,6 +27,8 @@ sbi bus controller + 04-Feb-2011 MP Added RQB, RQC and RQD as bootable controllers + 07-Jan-2011 MP Implemented reboot functionality in the console emulator 31-May-2008 RMS Fixed machine_check calling sequence (found by Peter Schorn) 03-May-2006 RMS Fixed writes to ACCS 28-May-08 RMS Inlined physical memory routines @@ -109,6 +111,9 @@ static struct boot_dev boot_tab[] = { { "HK", BOOT_HK, 0 }, { "RL", BOOT_RL, 0 }, { "RQ", BOOT_UDA, 1 << 24 }, + { "RQB", BOOT_UDA, 1 << 24 }, + { "RQC", BOOT_UDA, 1 << 24 }, + { "RQD", BOOT_UDA, 1 << 24 }, { "TQ", BOOT_TK, 1 << 24 }, { NULL } }; @@ -152,6 +157,7 @@ extern void init_mbus_tab (void); extern void init_ubus_tab (void); extern t_stat build_mbus_tab (DEVICE *dptr, DIB *dibp); extern t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp); +extern DEVICE cpu_dev; /* SBI data structures @@ -162,6 +168,8 @@ extern t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp); UNIT sbi_unit = { UDATA (NULL, 0, 0) }; +char cpu_boot_command[64]; + REG sbi_reg[] = { { HRDATA (NREQ14, nexus_req[0], 16) }, { HRDATA (NREQ15, nexus_req[1], 16) }, @@ -175,6 +183,7 @@ REG sbi_reg[] = { { HRDATA (SBIMT, sbi_mt, 32) }, { HRDATA (SBIER, sbi_er, 32) }, { HRDATA (SBITMO, sbi_tmo, 32) }, + { BRDATA (BOOTCMD, cpu_boot_command, 16, 8, sizeof(cpu_boot_command)), REG_HRO }, { NULL } }; @@ -587,14 +596,6 @@ sbi_er = sbi_er & ~SBIER_TMOW1C; /* clr SBIER etc */ return cc; } -/* Console entry */ - -int32 con_halt (int32 code, int32 cc) -{ -ABORT (STOP_HALT); -return cc; -} - /* Special boot command - linked into SCP by initial reset Syntax: BOOT {/R5:val} @@ -602,7 +603,7 @@ return cc; Sets up R0-R5, calls SCP boot processor with effective BOOT CPU */ -t_stat vax780_boot (int32 flag, char *ptr) +t_stat vax780_boot_parse (int32 flag, char *ptr) { char gbuf[CBUFSIZE]; char *slptr, *regptr; @@ -649,12 +650,23 @@ for (i = 0; boot_tab[i].name != NULL; i++) { R[3] = unitno; R[4] = 0; R[5] = r5v; - return run_cmd (flag, "CPU"); + strncpy(cpu_boot_command, ptr, sizeof(cpu_boot_command)-1); + return SCPE_OK; } } return SCPE_NOFNC; } +t_stat vax780_boot (int32 flag, char *ptr) +{ +t_stat r; + +r = vax780_boot_parse (flag, ptr); +if (r == SCPE_OK) + return run_cmd (flag, "CPU"); +return r; +} + /* Bootstrap - finish up bootstrap process */ t_stat cpu_boot (int32 unitno, DEVICE *dptr) @@ -671,6 +683,21 @@ SP = PC = 512; return SCPE_OK; } +/* Console entry/Reboot */ + +int32 con_halt (int32 code, int32 cc) +{ +t_stat r; + +printf ("Reboot Requested ... Rebooting\n"); +if (sim_log) fprintf (sim_log, + "Reboot Requested ... Rebooting\n"); +reset_all (0); +r = vax780_boot_parse (4, cpu_boot_command); +if (r == SCPE_OK) r = cpu_boot (0, &cpu_dev); +return r; +} + /* SBI reset */ t_stat sbi_reset (DEVICE *dptr) diff --git a/VAX/vax780_stddev.c b/VAX/vax780_stddev.c index 512ac36c..7887187d 100644 --- a/VAX/vax780_stddev.c +++ b/VAX/vax780_stddev.c @@ -284,6 +284,10 @@ REG clk_reg[] = { { DRDATA (TODR, todr_reg, 32), PV_LEFT }, { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, { DRDATA (TPS, clk_tps, 8), REG_HIDDEN + REG_NZ + PV_LEFT }, +#if defined (SIM_ASYNCH_IO) + { DRDATA (LATENCY, sim_asynch_latency, 32), PV_LEFT }, + { DRDATA (INST_LATENCY, sim_asynch_inst_latency, 32), PV_LEFT }, +#endif { NULL } }; @@ -570,6 +574,7 @@ t_stat clk_svc (UNIT *uptr) tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ sim_activate (&clk_unit, tmr_poll); /* reactivate unit */ tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */ +AIO_SET_INTERRUPT_LATENCY(tmr_poll*clk_tps); /* set interrrupt latency */ todr_reg = todr_reg + 1; /* incr TODR */ if ((tmr_iccs & TMR_CSR_RUN) && tmr_use_100hz) /* timer on, std intvl? */ tmr_incr (TMR_INC); /* do timer service */ diff --git a/VAX/vax_cpu.c b/VAX/vax_cpu.c index 933040f3..e082020a 100644 --- a/VAX/vax_cpu.c +++ b/VAX/vax_cpu.c @@ -25,6 +25,7 @@ cpu VAX central processor + 05-Jan-11 MP Added Asynch I/O support 24-Apr-10 RMS Added OLDVMS idle timer option Fixed bug in SET CPU IDLE 21-May-08 RMS Removed inline support @@ -612,6 +613,7 @@ for ( ;; ) { } fault_PC = PC; recqptr = 0; /* clr recovery q */ + AIO_CHECK_EVENT; if (sim_interval <= 0) { /* chk clock queue */ temp = sim_process_event (); if (temp) @@ -3108,6 +3110,7 @@ PSL = PSL_IS | PSL_IPL1F; SISR = 0; ASTLVL = 4; mapen = 0; +FLUSH_ISTR; if (M == NULL) M = (uint32 *) calloc (((uint32) MEMSIZE) >> 2, sizeof (uint32)); if (M == NULL) diff --git a/VAX/vax_stddev.c b/VAX/vax_stddev.c index fb092607..881fcefb 100644 --- a/VAX/vax_stddev.c +++ b/VAX/vax_stddev.c @@ -27,6 +27,7 @@ tto terminal output clk 100Hz and TODR clock + 05-Jan-11 MP Added Asynch I/O support 17-Aug-08 RMS Resync TODR on any clock reset 18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock 17-Oct-06 RMS Synced keyboard poll to real-time clock for idling @@ -178,6 +179,10 @@ REG clk_reg[] = { { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, { DRDATA (POLL, tmr_poll, 24), REG_NZ + PV_LEFT + REG_HRO }, { DRDATA (TPS, clk_tps, 8), REG_NZ + PV_LEFT }, +#if defined (SIM_ASYNCH_IO) + { DRDATA (LATENCY, sim_asynch_latency, 32), PV_LEFT }, + { DRDATA (INST_LATENCY, sim_asynch_inst_latency, 32), PV_LEFT }, +#endif { NULL } }; diff --git a/scp.c b/scp.c index f64196a2..de96a3b8 100644 --- a/scp.c +++ b/scp.c @@ -23,6 +23,11 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 29-Jan-11 MP Adjusted sim_debug to: + - include the simulator timestamp (sim_time) + as part of the prefix for each line of output + - write complete lines at a time (avoid asynch I/O issues). + 05-Jan-11 MP Added Asynch I/O support 08-Feb-09 RMS Fixed warnings in help printouts 29-Dec-08 RMS Fixed implementation of MTAB_NC 24-Nov-08 RMS Revised RESTORE unit logic for consistency @@ -241,6 +246,17 @@ else if (sim_switches & SWMASK ('H')) val = 16; \ else val = dft; +/* Asynch I/O support */ +#if defined (SIM_ASYNCH_IO) +pthread_mutex_t sim_asynch_lock = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t sim_asynch_wake = PTHREAD_COND_INITIALIZER; +pthread_t sim_asynch_main_threadid; +struct sim_unit *sim_asynch_queue = NULL; +int32 sim_asynch_check; +int32 sim_asynch_latency = 4000; /* 4 usec interrupt latency */ +int32 sim_asynch_inst_latency = 20; /* assume 5 mip simulator */ +#endif + /* VM interface */ extern char sim_name[]; @@ -393,7 +409,7 @@ static const char *sim_sa64 = "64b addresses"; #else static const char *sim_sa64 = "32b addresses"; #endif -#if defined USE_NETWORK +#if defined (USE_NETWORK) || defined (USE_SHARED) static const char *sim_snet = "Ethernet support"; #else static const char *sim_snet = "no Ethernet"; @@ -616,6 +632,7 @@ for (i = 1; i < argc; i++) { /* loop thru args */ } /* end for */ sim_quiet = sim_switches & SWMASK ('Q'); /* -q means quiet */ +AIO_INIT; /* init Asynch I/O */ if (sim_vm_init != NULL) /* call once only */ (*sim_vm_init)(); sim_finit (); /* init fio package */ @@ -700,6 +717,7 @@ sim_set_deboff (0, NULL); /* close debug */ sim_set_logoff (0, NULL); /* close log */ sim_set_notelnet (0, NULL); /* close Telnet */ sim_ttclose (); /* close console */ +AIO_CLEANUP; /* Asynch I/O */ return 0; } @@ -1564,6 +1582,26 @@ for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) { fprintf (st, " at %d\n", accum + uptr->time); accum = accum + uptr->time; } +#if defined (SIM_ASYNCH_IO) +pthread_mutex_lock (&sim_asynch_lock); +fprintf (st, "asynchronous pending event queue\n"); +if (sim_asynch_queue == (void *)-1) + fprintf (st, "Empty\n"); +else { + for (uptr = sim_asynch_queue; uptr != (void *)-1; uptr = uptr->a_next) { + if ((dptr = find_dev_from_unit (uptr)) != NULL) { + fprintf (st, " %s", sim_dname (dptr)); + if (dptr->numunits > 1) fprintf (st, " unit %d", + (int32) (uptr - dptr->units)); + } + else fprintf (st, " Unknown"); + fprintf (st, " event delay %d, queue time %d\n", uptr->a_event_time, uptr->a_sim_interval); + } + } +fprintf (st, "asynch latency: %d microseconds\n", sim_asynch_latency); +fprintf (st, "asynch instruction latency: %d instructions\n", sim_asynch_inst_latency); +pthread_mutex_unlock (&sim_asynch_lock); +#endif return SCPE_OK; } @@ -2633,14 +2671,6 @@ r = sim_instr(); sim_is_running = 0; /* flag idle */ sim_ttcmd (); /* restore console */ signal (SIGINT, SIG_DFL); /* cancel WRU */ -sim_cancel (&sim_step_unit); /* cancel step timer */ -sim_throt_cancel (); /* cancel throttle */ -if (sim_clock_queue != NULL) { /* update sim time */ - UPDATE_SIM_TIME (sim_clock_queue->time); - } -else { - UPDATE_SIM_TIME (noqueue_time); - } if (sim_log) /* flush console log */ fflush (sim_log); if (sim_deb) /* flush debug log */ @@ -2650,12 +2680,24 @@ for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* flush attached files uptr = dptr->units + j; if ((uptr->flags & UNIT_ATT) && /* attached, */ !(uptr->flags & UNIT_BUF) && /* not buffered, */ - (uptr->fileref) && /* real file, */ - !(uptr->flags & UNIT_RAW) && /* not raw, */ - !(uptr->flags & UNIT_RO)) /* not read only? */ - fflush (uptr->fileref); + (uptr->fileref)) /* real file, */ + if (uptr->io_flush) /* unit specific flush routine */ + uptr->io_flush (uptr); + else + if (!(uptr->flags & UNIT_RAW) && /* not raw, */ + !(uptr->flags & UNIT_RO)) /* not read only? */ + fflush (uptr->fileref); } } +sim_cancel (&sim_step_unit); /* cancel step timer */ +sim_throt_cancel (); /* cancel throttle */ +AIO_UPDATE_QUEUE; +if (sim_clock_queue != NULL) { /* update sim time */ + UPDATE_SIM_TIME (sim_clock_queue->time); + } +else { + UPDATE_SIM_TIME (noqueue_time); + } #if defined (VMS) printf ("\n"); #endif @@ -4159,6 +4201,7 @@ t_stat reason; if (stop_cpu) /* stop CPU? */ return SCPE_STOP; +AIO_UPDATE_QUEUE; if (sim_clock_queue == NULL) { /* queue empty? */ UPDATE_SIM_TIME (noqueue_time); /* update sim time */ sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */ @@ -4197,8 +4240,7 @@ t_stat sim_activate (UNIT *uptr, int32 event_time) UNIT *cptr, *prvptr; int32 accum; -if (event_time < 0) - return SCPE_IERR; +AIO_ACTIVATE (sim_activate, uptr, event_time); if (sim_is_active (uptr)) /* already active? */ return SCPE_OK; if (sim_clock_queue == NULL) { @@ -4242,6 +4284,7 @@ return SCPE_OK; t_stat sim_activate_abs (UNIT *uptr, int32 event_time) { +AIO_ACTIVATE (sim_activate_abs, uptr, event_time); sim_cancel (uptr); return sim_activate (uptr, event_time); } @@ -4259,6 +4302,7 @@ t_stat sim_cancel (UNIT *uptr) { UNIT *cptr, *nptr; +AIO_VALIDATE; if (sim_clock_queue == NULL) return SCPE_OK; UPDATE_SIM_TIME (sim_clock_queue->time); /* update sim time */ @@ -4296,6 +4340,7 @@ int32 sim_is_active (UNIT *uptr) UNIT *cptr; int32 accum; +AIO_VALIDATE; accum = 0; for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) { if (cptr == sim_clock_queue) { @@ -4667,7 +4712,7 @@ return; /* Debug printout routines, from Dave Hittner */ const char* debug_bstates = "01_^"; -const char* debug_fmt = "DBG> %s %s: "; +const char* debug_fmt = "DBG(%.0f)> %s %s: "; int32 debug_unterm = 0; /* Finds debug phrase matching bitmask from from device DEBTAB table */ @@ -4697,7 +4742,7 @@ static void sim_debug_prefix (uint32 dbits, DEVICE* dptr) { if (!debug_unterm) { char* debug_type = get_dbg_verb (dbits, dptr); - fprintf(sim_deb, debug_fmt, dptr->name, debug_type); + fprintf(sim_deb, debug_fmt, sim_time, dptr->name, debug_type); } } @@ -4725,7 +4770,7 @@ if (sim_deb && (dptr->dctrl & dbits)) { #if defined (_WIN32) #define vsnprintf _vsnprintf #endif -#if defined (__DECC) && defined (__VMS) +#if defined (__DECC) && defined (__VMS) && defined (__VAX) #define NO_vsnprintf #endif #if defined( NO_vsnprintf) @@ -4738,9 +4783,12 @@ if (sim_deb && (dptr->dctrl & dbits)) { set and the bitmask matches the current device debug options. Extra returns are added for un*x systems, since the output device is set into 'raw' mode when the cpu is booted, - and the extra returns don't hurt any other systems. */ - -void sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...) + and the extra returns don't hurt any other systems. + Callers should be calling sim_debug() which is a macro + defined in scp.h which evaluates the action condition before + incurring call overhead. */ + +void _sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...) { if (sim_deb && (dptr->dctrl & dbits)) { @@ -4749,9 +4797,9 @@ if (sim_deb && (dptr->dctrl & dbits)) { char *buf = stackbuf; va_list arglist; int32 i, j, len; + char* debug_type = get_dbg_verb (dbits, dptr); buf[bufsize-1] = '\0'; - sim_debug_prefix(dbits, dptr); /* print prefix if required */ while (1) { /* format passed string, args */ va_start (arglist, fmt); @@ -4797,10 +4845,14 @@ if (sim_deb && (dptr->dctrl & dbits)) { for (i = j = 0; i < len; ++i) { if ('\n' == buf[i]) { - if (i > j) - fwrite (&buf[j], 1, i-j, sim_deb); - j = i; - fputc('\r', sim_deb); + if (i > j) { + if (debug_unterm) + fprintf (sim_deb, "%.*s\r\n", i-j, &buf[j]); + else /* print prefix when required */ + fprintf (sim_deb, "DBG(%.0f)> %s %s: %.*s\r\n", sim_time, dptr->name, debug_type, i-j, &buf[j]); + debug_unterm = 0; + } + j = i + 1; } } if (i > j) diff --git a/scp.h b/scp.h index cdb2c33a..84c352b1 100644 --- a/scp.h +++ b/scp.h @@ -23,6 +23,7 @@ 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. + 05-Dec-10 MP Added macro invocation of sim_debug 09-Aug-06 JDB Added assign_device and deassign_device 14-Jul-06 RMS Added sim_activate_abs 06-Jan-06 RMS Added fprint_stopped_gen @@ -116,7 +117,14 @@ char *match_ext (char *fnam, char *ext); t_stat sim_cancel_step (void); void sim_debug_u16 (uint32 dbits, DEVICE* dptr, const char* const* bitdefs, uint16 before, uint16 after, int terminate); +#ifdef CANT_USE_MACRO_VA_ARGS +#define _sim_debug sim_debug void sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...); +#else +void _sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...); +extern FILE *sim_deb; /* debug file */ +#define sim_debug(dbits, dptr, ...) if (sim_deb && ((dptr)->dctrl & dbits)) _sim_debug (dbits, dptr, __VA_ARGS__) +#endif void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr); #endif diff --git a/sim_defs.h b/sim_defs.h index 66f67d44..033eb03e 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 05-Jan-11 MP Added Asynch I/O support 21-Jul-08 RMS Removed inlining support 28-May-08 RMS Added inlining support 28-Jun-07 RMS Added IA64 VMS support (from Norm Lastovica) @@ -118,12 +119,16 @@ /* Length specific integer declarations */ +#if defined (VMS) +#include +#else typedef signed char int8; typedef signed short int16; typedef signed int int32; typedef unsigned char uint8; typedef unsigned short uint16; -typedef unsigned int uint32; +typedef unsigned int uint32; +#endif typedef int t_stat; /* status */ typedef int t_bool; /* boolean */ @@ -346,12 +351,23 @@ struct sim_unit { uint32 flags; /* flags */ t_addr capac; /* capacity */ t_addr pos; /* file position */ + void (*io_flush)(struct sim_unit *up);/* io flush routine */ + uint32 iostarttime; /* I/O start time */ int32 buf; /* buffer */ int32 wait; /* wait */ int32 u3; /* device specific */ int32 u4; /* device specific */ int32 u5; /* device specific */ int32 u6; /* device specific */ + void *up7; /* device specific */ + void *up8; /* device specific */ +#ifdef SIM_ASYNCH_IO + void (*a_check_completion)(struct sim_unit *); + struct sim_unit *a_next; /* next asynch active */ + int32 a_event_time; + int32 a_sim_interval; + t_stat (*a_activate_call)(struct sim_unit *, int32); +#endif }; /* Unit flags */ @@ -528,4 +544,157 @@ typedef struct sim_debtab DEBTAB; #include "sim_timer.h" #include "sim_fio.h" +/* Asynch/Threaded I/O support */ + +#if defined (SIM_ASYNCH_IO) +#include + +extern pthread_mutex_t sim_asynch_lock; +extern pthread_cond_t sim_asynch_wake; +extern pthread_t sim_asynch_main_threadid; +extern struct sim_unit *sim_asynch_queue; +extern t_bool sim_idle_wait; +extern int32 sim_asynch_check; +extern int32 sim_asynch_latency; +extern int32 sim_asynch_inst_latency; + +#define AIO_INIT \ + if (1) { \ + sim_asynch_main_threadid = pthread_self(); \ + /* Empty list/list end uses the point value (void *)-1. \ + This allows NULL in an entry's a_next pointer to \ + indicate that the entry is not currently in any list */ \ + sim_asynch_queue = (void *)-1; \ + } +#define AIO_CLEANUP \ + if (1) { \ + pthread_mutex_destroy(&sim_asynch_lock); \ + pthread_cond_destroy(&sim_asynch_wake); \ + } +#if defined(_WIN32) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) +#define USE_AIO_INTRINSICS 1 +#endif +#ifdef USE_AIO_INTRINSICS +/* This approach uses intrinsics to manage access to the link list head */ +/* sim_asynch_queue. However, once the list head state has been determined */ +/* a lock is used to manage the list update and entry removal. */ +/* This approach avoids the ABA issues with a completly lock free approach */ +/* since the ABA problem is very likely to happen with this use model, and */ +/* it avoids the lock overhead for the simple list head checking. */ +#ifdef _WIN32 +#include +#ifdef ERROR +#undef ERROR +#endif /* ERROR */ +#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) +#define InterlockedCompareExchangePointer(Destination, Exchange, Comparand) __sync_val_compare_and_swap(Destination, Comparand, Exchange) +#define InterlockedExchangePointer(Destination, value) __sync_lock_test_and_set(Destination, value) +#else +#error "Implementation of functions InterlockedCompareExchangePointer() and InterlockedExchangePointer() are needed to build with USE_AIO_INTRINSICS" +#endif +#define AIO_QUEUE_VAL InterlockedCompareExchangePointer(&sim_asynch_queue, sim_asynch_queue, NULL) +#define AIO_QUEUE_SET(val) InterlockedExchangePointer(&sim_asynch_queue, val) +#define AIO_UPDATE_QUEUE \ + if (1) { \ + UNIT *uptr; \ + if (AIO_QUEUE_VAL != (void *)-1) { \ + pthread_mutex_lock (&sim_asynch_lock); \ + while ((uptr = AIO_QUEUE_VAL) != (void *)-1) { \ + int32 a_event_time; \ + AIO_QUEUE_SET(uptr->a_next); \ + uptr->a_next = NULL; /* hygiene */ \ + a_event_time = uptr->a_event_time-(uptr->a_sim_interval-sim_interval); \ + if (a_event_time < 0) a_event_time = 0; \ + uptr->a_activate_call (uptr, a_event_time); \ + if (uptr->a_check_completion) { \ + pthread_mutex_unlock (&sim_asynch_lock); \ + uptr->a_check_completion (uptr); \ + pthread_mutex_lock (&sim_asynch_lock); \ + } \ + } \ + pthread_mutex_unlock (&sim_asynch_lock); \ + } \ + sim_asynch_check = sim_asynch_inst_latency; \ + } +#define AIO_ACTIVATE(caller, uptr, event_time) \ + if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \ + pthread_mutex_lock (&sim_asynch_lock); \ + if (uptr->a_next) { \ + uptr->a_activate_call = sim_activate_abs; \ + } else { \ + uptr->a_next = AIO_QUEUE_VAL; \ + uptr->a_event_time = event_time; \ + uptr->a_sim_interval = sim_interval; \ + uptr->a_activate_call = caller; \ + AIO_QUEUE_SET(uptr); \ + } \ + if (sim_idle_wait) \ + pthread_cond_signal (&sim_asynch_wake); \ + pthread_mutex_unlock (&sim_asynch_lock); \ + return SCPE_OK; \ + } +#else /* !USE_AIO_INTRINSICS */ +/* This approach uses a pthread mutex to manage access to the link list */ +/* head sim_asynch_queue. It will always work, but may be slower than the */ +/* partially lock free approach when using USE_AIO_INTRINSICS */ +#define AIO_UPDATE_QUEUE \ + if (1) { \ + UNIT *uptr; \ + pthread_mutex_lock (&sim_asynch_lock); \ + while (sim_asynch_queue != (void *)-1) { /* List !Empty */ \ + int32 a_event_time; \ + uptr = sim_asynch_queue; \ + sim_asynch_queue = uptr->a_next; \ + uptr->a_next = NULL; \ + a_event_time = uptr->a_event_time-(uptr->a_sim_interval-sim_interval); \ + if (a_event_time < 0) a_event_time = 0; \ + uptr->a_activate_call (uptr, a_event_time); \ + if (uptr->a_check_completion) { \ + pthread_mutex_unlock (&sim_asynch_lock); \ + uptr->a_check_completion (uptr); \ + pthread_mutex_lock (&sim_asynch_lock); \ + } \ + } \ + pthread_mutex_unlock (&sim_asynch_lock); \ + sim_asynch_check = sim_asynch_inst_latency; \ + } +#define AIO_ACTIVATE(caller, uptr, event_time) \ + if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \ + pthread_mutex_lock (&sim_asynch_lock); \ + if (uptr->a_next) { \ + uptr->a_activate_call = sim_activate_abs; \ + } else { \ + uptr->a_next = sim_asynch_queue; \ + uptr->a_event_time = event_time; \ + uptr->a_sim_interval = sim_interval; \ + uptr->a_activate_call = caller; \ + sim_asynch_queue = uptr; \ + } \ + if (sim_idle_wait) \ + pthread_cond_signal (&sim_asynch_wake); \ + pthread_mutex_unlock (&sim_asynch_lock); \ + return SCPE_OK; \ + } +#endif /* USE_AIO_INTRINSICS */ +#define AIO_VALIDATE if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) abort() +#define AIO_CHECK_EVENT \ + if (0 > --sim_asynch_check) { \ + AIO_UPDATE_QUEUE; \ + } +#define AIO_SET_INTERRUPT_LATENCY(instpersec) \ + if (1) { \ + sim_asynch_inst_latency = (int32)((((double)(instpersec))*sim_asynch_latency)/1000000000);\ + if (sim_asynch_inst_latency == 0) \ + sim_asynch_inst_latency = 1; \ + } +#else /* !SIM_ASYNCH_IO */ +#define AIO_UPDATE_QUEUE +#define AIO_ACTIVATE(caller, uptr, event_time) +#define AIO_VALIDATE +#define AIO_CHECK_EVENT +#define AIO_INIT +#define AIO_CLEANUP +#define AIO_SET_INTERRUPT_LATENCY(instpersec) +#endif /* SIM_ASYNCH_IO */ + #endif diff --git a/sim_disk.c b/sim_disk.c new file mode 100644 index 00000000..b6768525 --- /dev/null +++ b/sim_disk.c @@ -0,0 +1,3272 @@ +/* sim_disk.c: simulator disk support library + + Copyright (c) 2011, Mark Pizzolato + + 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 names of Mark Pizzolato shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Mark Pizzolato. + + + + This is the place which hides processing of various disk formats, + as well as OS-specific direct hardware access. + + 25-Jan-11 MP Initial Implemementation + +Public routines: + + sim_disk_attach attach disk unit + sim_disk_detach detach disk unit + sim_disk_rdsect read disk sectors + sim_disk_rdsect_a read disk sectors asynchronously + sim_disk_wrsect write disk sectors + sim_disk_wrsect_a write disk sectors asynchronously + sim_disk_unload unload or detach a disk as needed + sim_disk_reset reset unit + sim_disk_wrp TRUE if write protected + sim_disk_isavailable TRUE if available for I/O + sim_disk_size get disk size + sim_disk_set_fmt set disk format + sim_disk_show_fmt show disk format + sim_disk_set_capac set disk capacity + sim_disk_show_capac show disk capacity + sim_disk_set_async enable asynchronous operation + sim_disk_clr_async disable asynchronous operation + sim_disk_data_trace debug support + +Internal routines: + + sim_os_disk_open_raw platform specific open raw device + sim_os_disk_close_raw platform specific close raw device + sim_os_disk_size_raw platform specific raw device size + sim_os_disk_unload_raw platform specific disk unload/eject + sim_os_disk_rdsect platform specific read sectors + sim_os_disk_wrsect platform specific write sectors + + sim_vhd_disk_open platform independent open virtual disk file + sim_vhd_disk_create platform independent create virtual disk file + sim_vhd_disk_create_diff platform independent create differencing virtual disk file + sim_vhd_disk_close platform independent close virtual disk file + sim_vhd_disk_size platform independent virtual disk size + sim_vhd_disk_rdsect platform independent read virtual disk sectors + sim_vhd_disk_wrsect platform independent write virtual disk sectors + + +*/ + +#include "sim_defs.h" +#include "sim_disk.h" +#include +#include + +#ifdef _WIN32 +#include +#endif +#if defined SIM_ASYNCH_IO +#include +#endif + +extern FILE *sim_log; /* log file */ +extern int32 sim_switches; +extern int32 sim_quiet; +extern uint32 sim_taddr_64; +extern int32 sim_end; + +struct disk_context { + DEVICE *dptr; /* Device for unit (access to debug flags) */ + uint32 dbit; /* debugging bit */ + uint32 sector_size; /* Disk Sector Size (of the pseudo disk) */ + uint32 xfer_element_size; /* Disk Bus Transfer size (1 - byte, 2 - word, 4 - longword) */ + uint32 storage_sector_size;/* Sector size of the containing storage */ + uint32 removable; /* Removable device flag */ + uint32 auto_format; /* Format determined dynamically */ +#if defined _WIN32 + HANDLE disk_handle; /* OS specific Raw device handle */ +#endif +#if defined SIM_ASYNCH_IO + int asynch_io; /* Asynchronous Interrupt scheduling enabled */ + int asynch_io_latency; /* instructions to delay pending interrupt */ + pthread_mutex_t lock; + pthread_t io_thread; /* I/O Thread Id */ + pthread_mutex_t io_lock; + pthread_cond_t io_cond; + int io_dop; + uint8 *buf; + t_seccnt *rsects; + t_seccnt sects; + t_lba lba; + DISK_PCALLBACK callback; + t_stat io_status; +#endif + }; + +#define disk_ctx up8 /* Field in Unit structure which points to the disk_context */ + +#if defined SIM_ASYNCH_IO +#define AIO_CALLSETUP \ +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; \ + \ +if ((!callback) || !ctx->asynch_io) + +#define AIO_CALL(op, _lba, _buf, _rsects, _sects, _callback) \ + if (1) { \ + struct disk_context *ctx = \ + (struct disk_context *)uptr->disk_ctx; \ + \ + pthread_mutex_lock (&ctx->io_lock); \ + \ + sim_debug (ctx->dbit, ctx->dptr, \ + "sim_disk AIO_CALL(op=%d, unit=%d, lba=0x%X, sects=%d)\n",\ + op, uptr-ctx->dptr->units, _lba, _sects); \ + \ + if (ctx->callback) \ + abort(); /* horrible mistake, stop */ \ + ctx->io_dop = op; \ + ctx->lba = _lba; \ + ctx->buf = _buf; \ + ctx->sects = _sects; \ + ctx->rsects = _rsects; \ + ctx->callback = _callback; \ + pthread_cond_signal (&ctx->io_cond); \ + pthread_mutex_unlock (&ctx->io_lock); \ + } + + +#define DOP_DONE 0 /* close */ +#define DOP_RSEC 1 /* sim_disk_rdsect_a */ +#define DOP_WSEC 2 /* sim_disk_wrsect_a */ +#define DOP_IAVL 3 /* sim_disk_isavailable_a */ + +static void * +_disk_io(void *arg) +{ +UNIT* volatile uptr = (UNIT*)arg; +int sched_policy; +struct sched_param sched_priority; +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; + +/* Boost Priority for this I/O thread vs the CPU instruction execution + thread which in general won't be readily yielding the processor when + this thread needs to run */ +pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); +++sched_priority.sched_priority; +pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); + +sim_debug (ctx->dbit, ctx->dptr, "_disk_io(unit=%d) starting\n", uptr-ctx->dptr->units); + +pthread_mutex_lock (&ctx->io_lock); +while (ctx->asynch_io) { + pthread_cond_wait (&ctx->io_cond, &ctx->io_lock); + if (ctx->io_dop == DOP_DONE) + break; + pthread_mutex_unlock (&ctx->io_lock); + switch (ctx->io_dop) { + case DOP_RSEC: + ctx->io_status = sim_disk_rdsect (uptr, ctx->lba, ctx->buf, ctx->rsects, ctx->sects); + break; + case DOP_WSEC: + ctx->io_status = sim_disk_wrsect (uptr, ctx->lba, ctx->buf, ctx->rsects, ctx->sects); + break; + case DOP_IAVL: + ctx->io_status = sim_disk_isavailable (uptr); + break; + } + pthread_mutex_lock (&ctx->io_lock); + ctx->io_dop = DOP_DONE; + sim_activate (uptr, ctx->asynch_io_latency); + } +pthread_mutex_unlock (&ctx->io_lock); + +sim_debug (ctx->dbit, ctx->dptr, "_disk_io(unit=%d) exiting\n", uptr-ctx->dptr->units); + +return NULL; +} + +/* This routine is called in the context of the main simulator thread before + processing events for any unit. It is only called when an asynchronous + thread has called sim_activate() to activate a unit. The job of this + routine is to put the unit in proper condition to digest what may have + occurred in the asynchrcondition thread. + + Since disk processing only handles a single I/O at a time to a + particular disk device (due to using stdio for the SimH Disk format + and stdio doesn't have an atomic seek+(read|write) operation), + we have the opportunity to possibly detect improper attempts to + issue multiple concurrent I/O requests. */ +static void _disk_completion_dispatch (UNIT *uptr) +{ +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; +DISK_PCALLBACK callback = ctx->callback; + +sim_debug (ctx->dbit, ctx->dptr, "_disk_completion_dispatch(unit=%d, dop=%d, callback=%p)\n", uptr-ctx->dptr->units, ctx->io_dop, ctx->callback); + +if (ctx->io_dop != DOP_DONE) + abort(); /* horribly wrong, stop */ + +if (ctx->callback && ctx->io_dop == DOP_DONE) { + ctx->callback = NULL; + callback (uptr, ctx->io_status); + } +} +#else +#define AIO_CALLSETUP +#define AIO_CALL(op, _lba, _buf, _rsects, _sects, _callback) \ + if (_callback) \ + (_callback) (uptr, r); +#endif + +/* Forward declarations */ + +static t_stat sim_vhd_disk_implemented (void); +static FILE *sim_vhd_disk_open (const char *rawdevicename, const char *openmode); +static FILE *sim_vhd_disk_create (const char *szVHDPath, t_addr desiredsize); +static FILE *sim_vhd_disk_create_diff (const char *szVHDPath, const char *szParentVHDPath); +static int sim_vhd_disk_close (FILE *f); +static void sim_vhd_disk_flush (FILE *f); +static t_addr sim_vhd_disk_size (FILE *f); +static t_stat sim_vhd_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects); +static t_stat sim_vhd_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects); +static t_stat sim_vhd_disk_set_dtype (FILE *f, const char *dtype); +static const char *sim_vhd_disk_get_dtype (FILE *f); +static t_stat sim_os_disk_implemented_raw (void); +static FILE *sim_os_disk_open_raw (const char *rawdevicename, const char *openmode); +static int sim_os_disk_close_raw (FILE *f); +static void sim_os_disk_flush_raw (FILE *f); +static t_addr sim_os_disk_size_raw (FILE *f); +static t_stat sim_os_disk_unload_raw (FILE *f); +static t_bool sim_os_disk_isavailable_raw (FILE *f); +static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects); +static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects); +static t_stat sim_os_disk_info_raw (FILE *f, uint32 *sector_size, uint32 *removable); +static t_stat sim_disk_pdp11_bad_block (UNIT *uptr, int32 sec); + +struct sim_disk_fmt { + char *name; /* name */ + int32 uflags; /* unit flags */ + int32 fmtval; /* Format type value */ + t_stat (*impl_fnc)(void); /* Implemented Test Function */ + }; + +static struct sim_disk_fmt fmts[DKUF_N_FMT] = { + { "SIMH", 0, DKUF_F_STD, NULL}, + { "RAW", 0, DKUF_F_RAW, sim_os_disk_implemented_raw}, + { "VHD", 0, DKUF_F_VHD, sim_vhd_disk_implemented}, + { NULL, 0, 0} + }; + +/* Set disk format */ + +t_stat sim_disk_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 f; + +if (uptr == NULL) + return SCPE_IERR; +if (cptr == NULL) + return SCPE_ARG; +for (f = 0; f < DKUF_N_FMT && fmts[f].name; f++) { + if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) { + if ((fmts[f].impl_fnc) && (fmts[f].impl_fnc() != SCPE_OK)) + return SCPE_NOFNC; + uptr->flags = (uptr->flags & ~DKUF_FMT) | + (fmts[f].fmtval << DKUF_V_FMT) | fmts[f].uflags; + return SCPE_OK; + } + } +return SCPE_ARG; +} + +/* Show disk format */ + +t_stat sim_disk_show_fmt (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, f = DK_GET_FMT (uptr); + +for (i = 0; i < DKUF_N_FMT; i++) + if (fmts[i].fmtval == f) { + fprintf (st, "%s format", fmts[i].name); + return SCPE_OK; + } +fprintf (st, "invalid format"); +return SCPE_OK; +} + +/* Set disk capacity */ + +t_stat sim_disk_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +t_addr cap; +t_stat r; + +if ((cptr == NULL) || (*cptr == 0)) + return SCPE_ARG; +if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; +cap = (t_addr) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r); +if (r != SCPE_OK) + return SCPE_ARG; +uptr->capac = cap * ((t_addr) 1000000); +return SCPE_OK; +} + +/* Show disk capacity */ + +t_stat sim_disk_show_capac (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (uptr->capac) { + if (uptr->capac >= (t_addr) 1000000) + fprintf (st, "capacity=%dMB", (uint32) (uptr->capac / ((t_addr) 1000000))); + else if (uptr->capac >= (t_addr) 1000) + fprintf (st, "capacity=%dKB", (uint32) (uptr->capac / ((t_addr) 1000))); + else fprintf (st, "capacity=%dB", (uint32) uptr->capac); + } +else fprintf (st, "undefined capacity"); +return SCPE_OK; +} + +/* Test for available */ + +t_bool sim_disk_isavailable (UNIT *uptr) +{ +if (!(uptr->flags & UNIT_ATT)) /* attached? */ + return FALSE; +switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* SIMH format */ + return TRUE; + case DKUF_F_VHD: /* VHD format */ + return TRUE; + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + return sim_os_disk_isavailable_raw (uptr->fileref); + break; + default: + return FALSE; + } +} + +t_bool sim_disk_isavailable_a (UNIT *uptr, DISK_PCALLBACK callback) +{ +t_bool r = FALSE; +AIO_CALLSETUP + r = sim_disk_isavailable (uptr); +AIO_CALL(DOP_IAVL, 0, NULL, NULL, 0, callback); +return r; +} + +/* Test for write protect */ + +t_bool sim_disk_wrp (UNIT *uptr) +{ +return (uptr->flags & DKUF_WRP)? TRUE: FALSE; +} + +/* Get Disk size */ + +t_addr sim_disk_size (UNIT *uptr) +{ +switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* SIMH format */ + return sim_fsize_ex (uptr->fileref); + case DKUF_F_VHD: /* VHD format */ + return sim_vhd_disk_size (uptr->fileref); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + return sim_os_disk_size_raw (uptr->fileref); + break; + default: + return (t_addr)-1; + } +} + +/* Enable asynchronous operation */ + +t_stat sim_disk_set_async (UNIT *uptr, int latency) +{ +#if !defined(SIM_ASYNCH_IO) +char *msg = "Disk: can't operate asynchronously\r\n"; +printf ("%s", msg); +if (sim_log) fprintf (sim_log, "%s", msg); +return SCPE_NOFNC; +#else +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; +pthread_attr_t attr; + +ctx->asynch_io = 1; +ctx->asynch_io_latency = latency; +pthread_mutex_init (&ctx->io_lock, NULL); +pthread_cond_init (&ctx->io_cond, NULL); +pthread_attr_init(&attr); +pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); +pthread_create (&ctx->io_thread, &attr, _disk_io, (void *)uptr); +pthread_attr_destroy(&attr); +uptr->a_check_completion = _disk_completion_dispatch; +#endif +return SCPE_OK; +} + +/* Disable asynchronous operation */ + +t_stat sim_disk_clr_async (UNIT *uptr) +{ +#if !defined(SIM_ASYNCH_IO) +return SCPE_NOFNC; +#else +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; + +/* make sure device exists */ +if (!ctx) return SCPE_UNATT; + +if (ctx->asynch_io) { + pthread_mutex_lock (&ctx->io_lock); + ctx->asynch_io = 0; + pthread_cond_signal (&ctx->io_cond); + pthread_mutex_unlock (&ctx->io_lock); + pthread_join (ctx->io_thread, NULL); + pthread_mutex_destroy (&ctx->io_lock); + pthread_cond_destroy (&ctx->io_cond); + } +return SCPE_OK; +#endif +} + +/* Read Sectors */ + +static t_stat _sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +t_addr da; +uint32 err, tbc; +size_t i; +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; + +sim_debug (ctx->dbit, ctx->dptr, "_sim_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", uptr-ctx->dptr->units, lba, sects); + +da = ((t_addr)lba) * ctx->sector_size; +tbc = sects * ctx->sector_size; +if (sectsread) + *sectsread = 0; +err = sim_fseek (uptr->fileref, da, SEEK_SET); /* set pos */ +if (!err) { + i = sim_fread (buf, ctx->xfer_element_size, tbc/ctx->xfer_element_size, uptr->fileref); + if (i < tbc/ctx->xfer_element_size) /* fill */ + memset (&buf[i*ctx->xfer_element_size], 0, tbc-(i*ctx->xfer_element_size)); + err = ferror (uptr->fileref); + if ((!err) && (sectsread)) + *sectsread = (i*ctx->xfer_element_size+ctx->sector_size-1)/ctx->sector_size; + } +return err; +} + +t_stat sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +t_stat r; +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; +t_seccnt sread; + +sim_debug (ctx->dbit, ctx->dptr, "sim_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", uptr-ctx->dptr->units, lba, sects); + +if ((sects == 1) && /* Single sector reads */ + (lba >= uptr->capac/ctx->sector_size)) { /* beyond the end of the disk */ + memset (buf, '\0', ctx->sector_size); /* are bad block management efforts - zero buffer */ + if (sectsread) + *sectsread = 1; + return SCPE_OK; /* return success */ + } + +if ((0 == (ctx->sector_size & (ctx->storage_sector_size - 1))) || /* Sector Aligned & whole sector transfers */ + ((0 == ((lba*ctx->sector_size) & (ctx->storage_sector_size - 1))) && + (0 == ((sects*ctx->sector_size) & (ctx->storage_sector_size - 1))))) { + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* SIMH format */ + return _sim_disk_rdsect (uptr, lba, buf, sectsread, sects); + case DKUF_F_VHD: /* VHD format */ + r = sim_vhd_disk_rdsect (uptr, lba, buf, &sread, sects); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + r = sim_os_disk_rdsect (uptr, lba, buf, &sread, sects); + break; + default: + return SCPE_NOFNC; + } + if (sectsread) + *sectsread = sread; + if (r != SCPE_OK) + return r; + sim_buf_swap_data (buf, ctx->xfer_element_size, (sread * ctx->sector_size) / ctx->xfer_element_size); + return r; + } +else { /* Unaligned and/or partial sector transfers */ + uint8 *tbuf = malloc (sects*ctx->sector_size + 2*ctx->storage_sector_size); + t_lba sspsts = ctx->storage_sector_size/ctx->sector_size; /* sim sectors in a storage sector */ + t_lba tlba = lba & ~(sspsts - 1); + t_seccnt tsects = sects + (lba - tlba); + + tsects = (tsects + (sspsts - 1)) & ~(sspsts - 1); + if (sectsread) + *sectsread = 0; + if (tbuf == NULL) + return SCPE_MEM; + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* SIMH format */ + r = _sim_disk_rdsect (uptr, tlba, tbuf, &sread, tsects); + break; + case DKUF_F_VHD: /* VHD format */ + r = sim_vhd_disk_rdsect (uptr, tlba, tbuf, &sread, tsects); + if (r == SCPE_OK) + sim_buf_swap_data (tbuf, ctx->xfer_element_size, (sread * ctx->sector_size) / ctx->xfer_element_size); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + r = sim_os_disk_rdsect (uptr, tlba, tbuf, &sread, tsects); + if (r == SCPE_OK) + sim_buf_swap_data (tbuf, ctx->xfer_element_size, (sread * ctx->sector_size) / ctx->xfer_element_size); + break; + default: + free (tbuf); + return SCPE_NOFNC; + } + if (r == SCPE_OK) { + memcpy (buf, tbuf + ((lba - tlba) * ctx->sector_size), sects * ctx->sector_size); + if (sectsread) { + *sectsread = sread - (lba - tlba); + if (*sectsread > sects) + *sectsread = sects; + } + } + free (tbuf); + return r; + } +} + +t_stat sim_disk_rdsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects, DISK_PCALLBACK callback) +{ +t_stat r = SCPE_OK; +AIO_CALLSETUP + r = sim_disk_rdsect (uptr, lba, buf, sectsread, sects); +AIO_CALL(DOP_RSEC, lba, buf, sectsread, sects, callback); +return r; +} + +/* Write Sectors */ + +static t_stat _sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +t_addr da; +uint32 err, tbc; +size_t i; +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; + +sim_debug (ctx->dbit, ctx->dptr, "_sim_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", uptr-ctx->dptr->units, lba, sects); + +da = ((t_addr)lba) * ctx->sector_size; +tbc = sects * ctx->sector_size; +if (sectswritten) + *sectswritten = 0; +err = sim_fseek (uptr->fileref, da, SEEK_SET); /* set pos */ +if (!err) { + i = sim_fwrite (buf, ctx->xfer_element_size, tbc/ctx->xfer_element_size, uptr->fileref); + err = ferror (uptr->fileref); + if ((!err) && (sectswritten)) + *sectswritten = (i*ctx->xfer_element_size+ctx->sector_size-1)/ctx->sector_size; + } +return err; +} + +t_stat sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; +uint32 f = DK_GET_FMT (uptr); +t_stat r; +uint8 *tbuf = NULL; + +sim_debug (ctx->dbit, ctx->dptr, "sim_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", uptr-ctx->dptr->units, lba, sects); + +if (f == DKUF_F_STD) + return _sim_disk_wrsect (uptr, lba, buf, sectswritten, sects); +if ((0 == (ctx->sector_size & (ctx->storage_sector_size - 1))) || /* Sector Aligned & whole sector transfers */ + ((0 == ((lba*ctx->sector_size) & (ctx->storage_sector_size - 1))) && + (0 == ((sects*ctx->sector_size) & (ctx->storage_sector_size - 1))))) { + + if (sim_end || (ctx->xfer_element_size == sizeof (char))) + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_VHD: /* VHD format */ + return sim_vhd_disk_wrsect (uptr, lba, buf, sectswritten, sects); + case DKUF_F_RAW: /* Raw Physical Disk Access */ + return sim_os_disk_wrsect (uptr, lba, buf, sectswritten, sects); + default: + return SCPE_NOFNC; + } + + tbuf = malloc (sects * ctx->sector_size); + if (NULL == tbuf) + return SCPE_MEM; + sim_buf_copy_swapped (tbuf, buf, ctx->xfer_element_size, (sects * ctx->sector_size) / ctx->xfer_element_size); + + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_VHD: /* VHD format */ + r = sim_vhd_disk_wrsect (uptr, lba, tbuf, sectswritten, sects); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + r = sim_os_disk_wrsect (uptr, lba, tbuf, sectswritten, sects); + break; + default: + r = SCPE_NOFNC; + break; + } + } +else { /* Unaligned and/or partial sector transfers */ + t_lba sspsts = ctx->storage_sector_size/ctx->sector_size; /* sim sectors in a storage sector */ + t_lba tlba = lba & ~(sspsts - 1); + t_seccnt tsects = sects + (lba - tlba); + + tbuf = malloc (sects*ctx->sector_size + 2*ctx->storage_sector_size); + tsects = (tsects + (sspsts - 1)) & ~(sspsts - 1); + if (sectswritten) + *sectswritten = 0; + if (tbuf == NULL) + return SCPE_MEM; + /* Partial Sector writes require a read-modify-write sequence for the partial sectors */ + if ((lba & (sspsts - 1)) || + (sects < sspsts)) + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_VHD: /* VHD format */ + sim_vhd_disk_rdsect (uptr, tlba, tbuf, NULL, sspsts); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + sim_os_disk_rdsect (uptr, tlba, tbuf, NULL, sspsts); + break; + default: + r = SCPE_NOFNC; + break; + } + if ((tsects > sspsts) && + ((sects + lba - tlba) & (sspsts - 1))) + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_VHD: /* VHD format */ + sim_vhd_disk_rdsect (uptr, tlba + tsects - sspsts, + tbuf + (tsects - sspsts) * ctx->sector_size, + NULL, sspsts); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + sim_os_disk_rdsect (uptr, tlba + tsects - sspsts, + tbuf + (tsects - sspsts) * ctx->sector_size, + NULL, sspsts); + break; + default: + r = SCPE_NOFNC; + break; + } + sim_buf_copy_swapped (tbuf + (lba & (sspsts - 1)) * ctx->sector_size, + buf, ctx->xfer_element_size, (sects * ctx->sector_size) / ctx->xfer_element_size); + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_VHD: /* VHD format */ + r = sim_vhd_disk_wrsect (uptr, tlba, tbuf, sectswritten, tsects); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + r = sim_os_disk_wrsect (uptr, tlba, tbuf, sectswritten, tsects); + break; + default: + r = SCPE_NOFNC; + break; + } + if ((r == SCPE_OK) && sectswritten) { + *sectswritten -= (lba - tlba); + if (*sectswritten > sects) + *sectswritten = sects; + } + } +free (tbuf); +return r; +} + +t_stat sim_disk_wrsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects, DISK_PCALLBACK callback) +{ +t_stat r = SCPE_OK; +AIO_CALLSETUP + r = sim_disk_wrsect (uptr, lba, buf, sectswritten, sects); +AIO_CALL(DOP_WSEC, lba, buf, sectswritten, sects, callback); +return r; +} + +t_stat sim_disk_unload (UNIT *uptr) +{ +switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* Simh */ + case DKUF_F_VHD: /* VHD format */ + return sim_disk_detach (uptr); + case DKUF_F_RAW: /* Raw Physical Disk Access */ + return sim_os_disk_unload_raw (uptr->fileref); /* remove/eject disk */ + break; + default: + return SCPE_NOFNC; + } +} + +static void _sim_disk_io_flush (UNIT *uptr) +{ +uint32 f = DK_GET_FMT (uptr); + +#if defined (SIM_ASYNCH_IO) +sim_disk_clr_async (uptr); +sim_disk_set_async (uptr, 0); +#endif +switch (f) { /* case on format */ + case DKUF_F_STD: /* Simh */ + fflush (uptr->fileref); + break; + case DKUF_F_VHD: /* Virtual Disk */ + sim_vhd_disk_flush (uptr->fileref); + break; + case DKUF_F_RAW: /* Physical */ + sim_os_disk_flush_raw (uptr->fileref); + break; + } +} + +static t_stat _err_return (UNIT *uptr, t_stat stat) +{ +free (uptr->filename); +uptr->filename = NULL; +free (uptr->disk_ctx); +uptr->disk_ctx = NULL; +return stat; +} + + +t_stat sim_disk_attach (UNIT *uptr, char *cptr, size_t sector_size, size_t xfer_element_size, t_bool dontautosize, uint32 dbit, const char *dtype, uint32 pdp11tracksize) +{ +struct disk_context *ctx; +DEVICE *dptr; +FILE *(*open_function)(const char *filename, const char *mode) = sim_fopen; +FILE *(*create_function)(const char *filename, t_addr desiredsize) = NULL; +t_addr (*size_function)(FILE *file); +t_stat (*storage_function)(FILE *file, uint32 *sector_size, uint32 *removable) = NULL; +t_bool created = FALSE; +t_bool auto_format = FALSE; +t_addr capac; + +if (uptr->flags & UNIT_DIS) /* disabled? */ + return SCPE_UDIS; +if (!(uptr->flags & UNIT_ATTABLE)) /* not attachable? */ + return SCPE_NOATT; +if ((dptr = find_dev_from_unit (uptr)) == NULL) + return SCPE_NOATT; +if (sim_switches & SWMASK ('F')) { /* format spec? */ + char gbuf[CBUFSIZE]; + cptr = get_glyph (cptr, gbuf, 0); /* get spec */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + if (sim_disk_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK) + return SCPE_ARG; + } +if (sim_switches & SWMASK ('D')) { /* create difference disk? */ + char gbuf[CBUFSIZE]; + FILE *vhd; + + sim_switches = sim_switches & ~(SWMASK ('D')); + cptr = get_glyph_nc (cptr, gbuf, 0); /* get spec */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + vhd = sim_vhd_disk_create_diff (gbuf, cptr); + if (vhd) { + sim_vhd_disk_close (vhd); + return sim_disk_attach (uptr, gbuf, sector_size, xfer_element_size, dontautosize, dbit, dtype, pdp11tracksize); + } + return SCPE_ARG; + } +if (sim_switches & SWMASK ('C')) { /* create vhd disk & copy contents? */ + char gbuf[CBUFSIZE]; + FILE *vhd; + int saved_sim_switches = sim_switches; + int32 saved_sim_quiet = sim_quiet; + t_stat r; + + sim_switches = sim_switches & ~(SWMASK ('C')); + cptr = get_glyph_nc (cptr, gbuf, 0); /* get spec */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + sim_switches |= SWMASK ('R') | SWMASK ('E'); + sim_quiet = TRUE; + /* First open the source of the copy operation */ + r = sim_disk_attach (uptr, cptr, sector_size, xfer_element_size, dontautosize, dbit, dtype, pdp11tracksize); + sim_quiet = saved_sim_quiet; + if (r != SCPE_OK) { + sim_switches = saved_sim_switches; + return r; + } + if (!sim_quiet) + printf ("%s%d: creating new virtual disk '%s'\n", sim_dname (dptr), (int)(uptr-dptr->units), gbuf); + vhd = sim_vhd_disk_create (gbuf, uptr->capac); + if (!vhd) { + if (!sim_quiet) + printf ("%s%d: can't create virtual disk '%s'\n", sim_dname (dptr), (int)(uptr-dptr->units), gbuf); + return SCPE_OPENERR; + } + else { + uint8 *copy_buf = malloc (1024*1024); + t_lba lba; + t_seccnt sectors_per_buffer = (1024*1024)/sector_size; + t_lba total_sectors = (t_lba)(uptr->capac/sector_size); + t_seccnt sects = sectors_per_buffer; + + if (!copy_buf) { + remove (gbuf); + return SCPE_MEM; + } + for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) { + if (!sim_quiet) + printf ("%s%d: Copied %dMB. %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)(((t_addr)lba*sector_size)/1000000), (int)((lba*100)/total_sectors)); + sects = sectors_per_buffer; + if (lba + sects > total_sectors) + sects = total_sectors - lba; + r = sim_disk_rdsect (uptr, lba, copy_buf, NULL, sects); + if (r == SCPE_OK) { + uint32 saved_unit_flags = uptr->flags; + FILE *save_unit_fileref = uptr->fileref; + + sim_disk_set_fmt (uptr, 0, "VHD", NULL); + uptr->fileref = vhd; + r = sim_disk_wrsect (uptr, lba, copy_buf, NULL, sects); + uptr->fileref = save_unit_fileref; + uptr->flags = saved_unit_flags; + } + } + if (!sim_quiet) + printf ("\n%s%d: Copied %dMB. Done.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)(((t_addr)lba*sector_size)/1000000)); + free (copy_buf); + created = TRUE; + sim_vhd_disk_close (vhd); + sim_disk_detach (uptr); + strcpy (cptr, gbuf); + sim_disk_set_fmt (uptr, 0, "VHD", NULL); + sim_switches = saved_sim_switches; + /* fall through and open/return the newly created & copied vhd */ + } + } +switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* SIMH format */ + if (NULL == (uptr->fileref = sim_vhd_disk_open (cptr, "rb"))) { + open_function = sim_fopen; + size_function = sim_fsize_ex; + break; + } + sim_disk_set_fmt (uptr, 0, "VHD", NULL); /* set file format to VHD */ + sim_vhd_disk_close (uptr->fileref); /* close vhd file*/ + auto_format = TRUE; + uptr->fileref = NULL; + /* Fall through to normal VHD processing */ + case DKUF_F_VHD: /* VHD format */ + open_function = sim_vhd_disk_open; + create_function = sim_vhd_disk_create; + size_function = sim_vhd_disk_size; + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + open_function = sim_os_disk_open_raw; + size_function = sim_os_disk_size_raw; + storage_function = sim_os_disk_info_raw; + break; + default: + return SCPE_IERR; + } +uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char));/* alloc name buf */ +uptr->disk_ctx = ctx = (struct disk_context *)calloc(1, sizeof(struct disk_context)); +if ((uptr->filename == NULL) || (uptr->disk_ctx == NULL)) + return _err_return (uptr, SCPE_MEM); +strncpy (uptr->filename, cptr, CBUFSIZE); /* save name */ +ctx->sector_size = sector_size; /* save sector_size */ +ctx->xfer_element_size = xfer_element_size; /* save xfer_element_size */ +ctx->dptr = dptr; /* save DEVICE pointer */ +ctx->dbit = dbit; /* save debug bit */ +ctx->auto_format = auto_format; /* save that we auto selected format */ +ctx->storage_sector_size = sector_size; /* Default */ +if (sim_switches & SWMASK ('R')) { /* read only? */ + if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */ + return _err_return (uptr, SCPE_NORO); /* no, error */ + uptr->fileref = open_function (cptr, "rb"); /* open rd only */ + if (uptr->fileref == NULL) /* open fail? */ + return _err_return (uptr, SCPE_OPENERR); /* yes, error */ + uptr->flags = uptr->flags | UNIT_RO; /* set rd only */ + if (!sim_quiet) + printf ("%s%d: unit is read only\n", sim_dname (dptr), (int)(uptr-dptr->units)); + } +else { /* normal */ + uptr->fileref = open_function (cptr, "rb+"); /* open r/w */ + if (uptr->fileref == NULL) { /* open fail? */ + if ((errno == EROFS) || (errno == EACCES)) { /* read only? */ + if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */ + return _err_return (uptr, SCPE_NORO); /* no error */ + uptr->fileref = open_function (cptr, "rb"); /* open rd only */ + if (uptr->fileref == NULL) /* open fail? */ + return _err_return (uptr, SCPE_OPENERR);/* yes, error */ + uptr->flags = uptr->flags | UNIT_RO; /* set rd only */ + if (!sim_quiet) + printf ("%s%d: unit is read only\n", sim_dname (dptr), (int)(uptr-dptr->units)); + } + else { /* doesn't exist */ + if (sim_switches & SWMASK ('E')) /* must exist? */ + return _err_return (uptr, SCPE_OPENERR); /* yes, error */ + if (create_function) + uptr->fileref = create_function (cptr, uptr->capac);/* create new file */ + else + uptr->fileref = open_function (cptr, "wb+");/* open new file */ + if (uptr->fileref == NULL) /* open fail? */ + return _err_return (uptr, SCPE_OPENERR);/* yes, error */ + if (!sim_quiet) + printf ("%s%d: creating new file\n", sim_dname (dptr), (int)(uptr-dptr->units)); + created = TRUE; + } + } /* end if null */ + } /* end else */ +if (DK_GET_FMT (uptr) == DKUF_F_VHD) { + if ((created) && dtype) + sim_vhd_disk_set_dtype (uptr->fileref, dtype); + if (dtype && strcmp (dtype, sim_vhd_disk_get_dtype (uptr->fileref))) { + char cmd[32]; + + sprintf (cmd, "%s%d %s", dptr->name, (int)(uptr-dptr->units), sim_vhd_disk_get_dtype (uptr->fileref)); + set_cmd (0, cmd); + } + } +uptr->flags = uptr->flags | UNIT_ATT; +uptr->pos = 0; + +/* Get Device attributes if they are available */ +if (storage_function) + storage_function (uptr->fileref, &ctx->storage_sector_size, &ctx->removable); + +if (created) { + t_stat r = SCPE_OK; + uint8 *secbuf = calloc (1, ctx->sector_size); /* alloc temp sector buf */ + + /* + On a newly created disk, we write a zero sector to the last and the + first sectors. This serves 3 purposes: + 1) it avoids strange allocation delays writing newly allocated + storage at the end of the disk during simulator operation + 2) it allocates storage for the whole disk at creation time to + avoid strange failures which may happen during simulator execution + if the containing disk is full + 3) it leaves a Sinh Format disk at the intended size so it may + subsequently be autosized with the correct size. + */ + if (secbuf == NULL) + r = SCPE_MEM; + if (r == SCPE_OK) + r = sim_disk_wrsect (uptr, (t_lba)((uptr->capac - ctx->sector_size)/ctx->sector_size), secbuf, NULL, 1); /* Write Last Sector */ + if (r == SCPE_OK) + r = sim_disk_wrsect (uptr, (t_lba)(0), secbuf, NULL, 1); /* Write First Sector */ + free (secbuf); + if (r != SCPE_OK) { + sim_disk_detach (uptr); /* report error now */ + remove (cptr); /* remove the create file */ + return SCPE_OPENERR; + } + if (pdp11tracksize) + sim_disk_pdp11_bad_block (uptr, pdp11tracksize); + } + +capac = size_function (uptr->fileref); +if (capac && (capac != (t_addr)-1)) + if (dontautosize) { + if ((capac < uptr->capac) && (DKUF_F_STD != DK_GET_FMT (uptr))) { + if (!sim_quiet) { + printf ("%s%d: non expandable disk %s is smaller than simulated device (", sim_dname (dptr), (int)(uptr-dptr->units), cptr); + fprint_val (stdout, capac, 10, T_ADDR_W, PV_LEFT); + printf (" < "); + fprint_val (stdout, uptr->capac, 10, T_ADDR_W, PV_LEFT); + printf (")\n"); + } + } + } + else + if ((capac > uptr->capac) || (DKUF_F_STD != DK_GET_FMT (uptr))) + uptr->capac = capac; + +#if defined (SIM_ASYNCH_IO) +sim_disk_set_async (uptr, 0); +#endif +uptr->io_flush = _sim_disk_io_flush; + +return SCPE_OK; +} + +t_stat sim_disk_detach (UNIT *uptr) +{ +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; +int (*close_function)(FILE *f); +FILE *fileref = uptr->fileref; +DEVICE *dptr; +t_bool auto_format; + +if (uptr == NULL) + return SCPE_IERR; +switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* Simh */ + close_function = fclose; + break; + case DKUF_F_VHD: /* Virtual Disk */ + close_function = sim_vhd_disk_close; + break; + case DKUF_F_RAW: /* Physical */ + close_function = sim_os_disk_close_raw; + break; + } +if (!(uptr->flags & UNIT_ATTABLE)) /* attachable? */ + return SCPE_NOATT; +if (!(uptr->flags & UNIT_ATT)) /* attached? */ + return SCPE_OK; +if ((dptr = find_dev_from_unit (uptr)) == NULL) + return SCPE_OK; +auto_format = ctx->auto_format; + +if (uptr->io_flush) + uptr->io_flush (uptr); /* flush buffered data */ + +#if defined SIM_ASYNCH_IO +sim_disk_clr_async (uptr); +#endif + +uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO | UNIT_RAW); +free (uptr->filename); +uptr->filename = NULL; +uptr->fileref = NULL; +free (uptr->disk_ctx); +uptr->disk_ctx = NULL; +uptr->io_flush = NULL; +if (auto_format) + sim_disk_set_fmt (uptr, 0, "SIMH", NULL); /* restore file format */ +if (close_function (fileref) == EOF) + return SCPE_IOERR; +return SCPE_OK; +} + +/* Factory bad block table creation routine + + This routine writes a DEC standard 044 compliant bad block table on the + last track of the specified unit. The bad block table consists of 10 + repetitions of the same table, formatted as follows: + + words 0-1 pack id number + words 2-3 cylinder/sector/surface specifications + : + words n-n+1 end of table (-1,-1) + + Inputs: + uptr = pointer to unit + sec = number of sectors per surface + Outputs: + sta = status code +*/ + +t_stat sim_disk_pdp11_bad_block (UNIT *uptr, int32 sec) +{ +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; +DEVICE *dptr; +int32 i; +t_addr da; +int32 wds = ctx->sector_size/sizeof (uint16); +uint16 *buf; + +if ((sec < 2) || (wds < 16)) + return SCPE_ARG; +if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; +if (uptr->flags & UNIT_RO) + return SCPE_RO; +if ((dptr = find_dev_from_unit (uptr)) == NULL) + return SCPE_NOATT; +if ((dptr->dwidth / dptr->aincr) <= 8) /* Must be Word oriented Capacity */ + return SCPE_IERR; +if (!get_yn ("Overwrite last track? [N]", FALSE)) + return SCPE_OK; +if ((buf = (uint16 *) malloc (wds * sizeof (uint16))) == NULL) + return SCPE_MEM; +buf[0] = buf[1] = 012345u; +buf[2] = buf[3] = 0; +for (i = 4; i < wds; i++) + buf[i] = 0177777u; +da = uptr->capac - (sec * wds); +for (i = 0; (i < sec) && (i < 10); i++, da += wds) + if (sim_disk_wrsect (uptr, (t_lba)(da/wds), (void *)buf, NULL, 1)) { + free (buf); + return SCPE_IOERR; + } +free (buf); +return SCPE_OK; +} + +void sim_disk_data_trace(UNIT *uptr, const uint8 *data, size_t lba, size_t len, const char* txt, int detail, uint32 reason) +{ +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; + +if (ctx->dptr->dctrl & reason) { + sim_debug (reason, ctx->dptr, "%s%d %s lbn: %08X len: %08X\n", ctx->dptr->name, uptr-ctx->dptr->units, txt, lba, len); + if (detail) { + size_t i, same, group, sidx, oidx; + char outbuf[80], strbuf[18]; + static char hex[] = "0123456789ABCDEF"; + + for (i=same=0; i 0) && (0 == memcmp (&data[i], &data[i-16], 16))) { + ++same; + continue; + } + if (same > 0) { + sim_debug (reason, ctx->dptr, "%04X thru %04X same as above\n", i-(16*same), i-1); + same = 0; + } + group = (((len - i) > 16) ? 16 : (len - i)); + for (sidx=oidx=0; sidx>4)&0xf]; + outbuf[oidx++] = hex[data[i+sidx]&0xf]; + if (isprint (data[i+sidx])) + strbuf[sidx] = data[i+sidx]; + else + strbuf[sidx] = '.'; + } + outbuf[oidx] = '\0'; + strbuf[sidx] = '\0'; + sim_debug (reason, ctx->dptr, "%04X%-48s %s\n", i, outbuf, strbuf); + } + if (same > 0) + sim_debug (reason, ctx->dptr, "%04X thru %04X same as above\n", i-(16*same), len-1); + } + } +} + + +/* OS Specific RAW Disk I/O support */ + +#if defined _WIN32 + +static void _set_errno_from_status (DWORD dwStatus) +{ +switch (dwStatus) { + case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_DRIVE: case ERROR_NO_MORE_FILES: + case ERROR_BAD_NET_NAME: case ERROR_BAD_NETPATH: + case ERROR_BAD_PATHNAME: case ERROR_FILENAME_EXCED_RANGE: + errno = ENOENT; + return; + case ERROR_INVALID_ACCESS: case ERROR_INVALID_DATA: + case ERROR_INVALID_FUNCTION: case ERROR_INVALID_PARAMETER: + case ERROR_NEGATIVE_SEEK: + errno = EINVAL; + return; + case ERROR_ARENA_TRASHED: case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_INVALID_BLOCK: case ERROR_NOT_ENOUGH_QUOTA: + errno = ENOMEM; + return; + case ERROR_TOO_MANY_OPEN_FILES: + errno = EMFILE; + return; + case ERROR_ACCESS_DENIED: case ERROR_CURRENT_DIRECTORY: + case ERROR_LOCK_VIOLATION: case ERROR_NETWORK_ACCESS_DENIED: + case ERROR_CANNOT_MAKE: case ERROR_FAIL_I24: + case ERROR_DRIVE_LOCKED: case ERROR_SEEK_ON_DEVICE: + case ERROR_NOT_LOCKED: case ERROR_LOCK_FAILED: + errno = EACCES; + return; + case ERROR_ALREADY_EXISTS: case ERROR_FILE_EXISTS: + errno = EEXIST; + return; + case ERROR_INVALID_HANDLE: case ERROR_INVALID_TARGET_HANDLE: + case ERROR_DIRECT_ACCESS_HANDLE: + errno = EBADF; + return; + case ERROR_DIR_NOT_EMPTY: + errno = ENOTEMPTY; + return; + case ERROR_BAD_ENVIRONMENT: + errno = E2BIG; + return; + case ERROR_BAD_FORMAT: + errno = ENOEXEC; + return; + case ERROR_NOT_SAME_DEVICE: + errno = EXDEV; + return; + case ERROR_BROKEN_PIPE: + errno = EPIPE; + return; + case ERROR_DISK_FULL: + errno = ENOSPC; + return; + case ERROR_WAIT_NO_CHILDREN: case ERROR_CHILD_NOT_COMPLETE: + errno = ECHILD; + return; + case ERROR_NO_PROC_SLOTS: case ERROR_MAX_THRDS_REACHED: + case ERROR_NESTING_NOT_ALLOWED: + errno = EAGAIN; + return; + } +if ((dwStatus >= ERROR_WRITE_PROTECT) && (dwStatus <= ERROR_SHARING_BUFFER_EXCEEDED)) { + errno = EACCES; + return; + } +if ((dwStatus >= ERROR_INVALID_STARTING_CODESEG) && (dwStatus <= ERROR_INFLOOP_IN_RELOC_CHAIN)) { + errno = ENOEXEC; + return; + } +errno = EINVAL; +} +#include +struct _device_type { + int32 Type; + char *desc; + } DeviceTypes[] = { + {FILE_DEVICE_8042_PORT, "8042_PORT"}, + {FILE_DEVICE_ACPI, "ACPI"}, + {FILE_DEVICE_BATTERY, "BATTERY"}, + {FILE_DEVICE_BEEP, "BEEP"}, +#ifdef FILE_DEVICE_BLUETOOTH + {FILE_DEVICE_BLUETOOTH, "BLUETOOTH"}, +#endif + {FILE_DEVICE_BUS_EXTENDER, "BUS_EXTENDER"}, + {FILE_DEVICE_CD_ROM, "CD_ROM"}, + {FILE_DEVICE_CD_ROM_FILE_SYSTEM, "CD_ROM_FILE_SYSTEM"}, + {FILE_DEVICE_CHANGER, "CHANGER"}, + {FILE_DEVICE_CONTROLLER, "CONTROLLER"}, +#ifdef FILE_DEVICE_CRYPT_PROVIDER + {FILE_DEVICE_CRYPT_PROVIDER, "CRYPT_PROVIDER"}, +#endif + {FILE_DEVICE_DATALINK, "DATALINK"}, + {FILE_DEVICE_DFS, "DFS"}, + {FILE_DEVICE_DFS_FILE_SYSTEM, "DFS_FILE_SYSTEM"}, + {FILE_DEVICE_DFS_VOLUME, "DFS_VOLUME"}, + {FILE_DEVICE_DISK, "DISK"}, + {FILE_DEVICE_DISK_FILE_SYSTEM, "DISK_FILE_SYSTEM"}, + {FILE_DEVICE_DVD, "DVD"}, + {FILE_DEVICE_FILE_SYSTEM, "FILE_SYSTEM"}, +#ifdef FILE_DEVICE_FIPS + {FILE_DEVICE_FIPS, "FIPS"}, +#endif + {FILE_DEVICE_FULLSCREEN_VIDEO, "FULLSCREEN_VIDEO"}, +#ifdef FILE_DEVICE_INFINIBAND + {FILE_DEVICE_INFINIBAND, "INFINIBAND"}, +#endif + {FILE_DEVICE_INPORT_PORT, "INPORT_PORT"}, + {FILE_DEVICE_KEYBOARD, "KEYBOARD"}, + {FILE_DEVICE_KS, "KS"}, + {FILE_DEVICE_KSEC, "KSEC"}, + {FILE_DEVICE_MAILSLOT, "MAILSLOT"}, + {FILE_DEVICE_MASS_STORAGE, "MASS_STORAGE"}, + {FILE_DEVICE_MIDI_IN, "MIDI_IN"}, + {FILE_DEVICE_MIDI_OUT, "MIDI_OUT"}, + {FILE_DEVICE_MODEM, "MODEM"}, + {FILE_DEVICE_MOUSE, "MOUSE"}, + {FILE_DEVICE_MULTI_UNC_PROVIDER, "MULTI_UNC_PROVIDER"}, + {FILE_DEVICE_NAMED_PIPE, "NAMED_PIPE"}, + {FILE_DEVICE_NETWORK, "NETWORK"}, + {FILE_DEVICE_NETWORK_BROWSER, "NETWORK_BROWSER"}, + {FILE_DEVICE_NETWORK_FILE_SYSTEM, "NETWORK_FILE_SYSTEM"}, + {FILE_DEVICE_NETWORK_REDIRECTOR, "NETWORK_REDIRECTOR"}, + {FILE_DEVICE_NULL, "NULL"}, + {FILE_DEVICE_PARALLEL_PORT, "PARALLEL_PORT"}, + {FILE_DEVICE_PHYSICAL_NETCARD, "PHYSICAL_NETCARD"}, + {FILE_DEVICE_PRINTER, "PRINTER"}, + {FILE_DEVICE_SCANNER, "SCANNER"}, + {FILE_DEVICE_SCREEN, "SCREEN"}, + {FILE_DEVICE_SERENUM, "SERENUM"}, + {FILE_DEVICE_SERIAL_MOUSE_PORT, "SERIAL_MOUSE_PORT"}, + {FILE_DEVICE_SERIAL_PORT, "SERIAL_PORT"}, + {FILE_DEVICE_SMARTCARD, "SMARTCARD"}, + {FILE_DEVICE_SMB, "SMB"}, + {FILE_DEVICE_SOUND, "SOUND"}, + {FILE_DEVICE_STREAMS, "STREAMS"}, + {FILE_DEVICE_TAPE, "TAPE"}, + {FILE_DEVICE_TAPE_FILE_SYSTEM, "TAPE_FILE_SYSTEM"}, + {FILE_DEVICE_TERMSRV, "TERMSRV"}, + {FILE_DEVICE_TRANSPORT, "TRANSPORT"}, + {FILE_DEVICE_UNKNOWN, "UNKNOWN"}, + {FILE_DEVICE_VDM, "VDM"}, + {FILE_DEVICE_VIDEO, "VIDEO"}, + {FILE_DEVICE_VIRTUAL_DISK, "VIRTUAL_DISK"}, +#ifdef FILE_DEVICE_VMBUS + {FILE_DEVICE_VMBUS, "VMBUS"}, +#endif + {FILE_DEVICE_WAVE_IN, "WAVE_IN"}, + {FILE_DEVICE_WAVE_OUT, "WAVE_OUT"}, +#ifdef FILE_DEVICE_WPD + {FILE_DEVICE_WPD, "WPD"}, +#endif + {0, NULL}}; + +static const char *_device_type_name (int DeviceType) +{ +int i; + +for (i=0; DeviceTypes[i].desc; i++) + if (DeviceTypes[i].Type == DeviceType) + return DeviceTypes[i].desc; +return "Unknown"; +} + +static t_stat sim_os_disk_implemented_raw (void) +{ +return SCPE_OK; +} + +static FILE *sim_os_disk_open_raw (const char *rawdevicename, const char *openmode) +{ +HANDLE Handle; +DWORD DesiredAccess = 0; + +if (strchr (openmode, 'r')) + DesiredAccess |= GENERIC_READ; +if (strchr (openmode, 'w') || strchr (openmode, '+')) + DesiredAccess |= GENERIC_WRITE; +Handle = CreateFileA (rawdevicename, DesiredAccess, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); +if (Handle == INVALID_HANDLE_VALUE) { + _set_errno_from_status (GetLastError ()); + return NULL; + } +return (FILE *)Handle; +} + +static int sim_os_disk_close_raw (FILE *f) +{ +if (!CloseHandle ((HANDLE)f)) { + _set_errno_from_status (GetLastError ()); + return EOF; + } +return 0; +} + +static void sim_os_disk_flush_raw (FILE *f) +{ +FlushFileBuffers ((HANDLE)f); +} + +static t_addr sim_os_disk_size_raw (FILE *Disk) +{ +DWORD IoctlReturnSize; +LARGE_INTEGER Size; +WINBASEAPI BOOL WINAPI GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize); + +if (GetFileSizeEx((HANDLE)Disk, &Size)) + return (t_addr)(Size.QuadPart); +#ifdef IOCTL_STORAGE_READ_CAPACITY +if (1) { + STORAGE_READ_CAPACITY S; + + ZeroMemory (&S, sizeof (S)); + S.Version = sizeof (STORAGE_READ_CAPACITY); + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_STORAGE_READ_CAPACITY, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &S, /* output buffer */ + (DWORD) sizeof(S), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + return (t_addr)(S.DiskLength.QuadPart); + } +#endif +#ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY_EX +if (1) { + DISK_GEOMETRY_EX G; + + ZeroMemory (&G, sizeof (G)); + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &G, /* output buffer */ + (DWORD) sizeof(G), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + return (t_addr)(G.DiskSize.QuadPart); + } +#endif +#ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY +if (1) { + DISK_GEOMETRY G; + + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_DISK_GET_DRIVE_GEOMETRY, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &G, /* output buffer */ + (DWORD) sizeof(G), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + return (t_addr)(G.Cylinders.QuadPart*G.TracksPerCylinder*G.SectorsPerTrack*G.BytesPerSector); + } +#endif +_set_errno_from_status (GetLastError ()); +return (t_addr)-1; +} + +static t_stat sim_os_disk_unload_raw (FILE *Disk) +{ +#ifdef IOCTL_STORAGE_EJECT_MEDIA +DWORD BytesReturned; + +if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */ + IOCTL_STORAGE_EJECT_MEDIA, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + NULL, /* lpOutBuffer */ + 0, /* nOutBufferSize */ + (LPDWORD) &BytesReturned, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) { /* OVERLAPPED structure */ + _set_errno_from_status (GetLastError ()); + return SCPE_IOERR; + } +return SCPE_OK; +#else +return SCPE_NOFNC; +#endif +} + +static t_bool sim_os_disk_isavailable_raw (FILE *Disk) +{ +#ifdef IOCTL_STORAGE_EJECT_MEDIA +DWORD BytesReturned; + +if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */ + IOCTL_STORAGE_CHECK_VERIFY, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + NULL, /* lpOutBuffer */ + 0, /* nOutBufferSize */ + (LPDWORD) &BytesReturned, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) { /* OVERLAPPED structure */ + _set_errno_from_status (GetLastError ()); + return FALSE; + } +#endif +return TRUE; +} + +static t_stat sim_os_disk_info_raw (FILE *Disk, size_t *sector_size, uint32 *removable) +{ +DWORD IoctlReturnSize; +#ifndef __GNUC__ +STORAGE_DEVICE_NUMBER Device; + +ZeroMemory (&Device, sizeof (Device)); +if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_STORAGE_GET_DEVICE_NUMBER, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &Device, /* output buffer */ + (DWORD) sizeof(Device), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + printf ("Device OK - Type: %s, Number: %d\n", _device_type_name (Device.DeviceType), Device.DeviceNumber); +#endif + +if (sector_size) + *sector_size = 512; +if (removable) + *removable = 0; +#ifdef IOCTL_STORAGE_READ_CAPACITY +if (1) { + STORAGE_READ_CAPACITY S; + + ZeroMemory (&S, sizeof (S)); + S.Version = sizeof (STORAGE_READ_CAPACITY); + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_STORAGE_READ_CAPACITY, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &S, /* output buffer */ + (DWORD) sizeof(S), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + if (sector_size) + *sector_size = S.BlockLength; + } +#endif +#ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY_EX +if (1) { + DISK_GEOMETRY_EX G; + + ZeroMemory (&G, sizeof (G)); + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &G, /* output buffer */ + (DWORD) sizeof(G), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + if (sector_size) + *sector_size = G.Geometry.BytesPerSector; + } +#endif +#ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY +if (1) { + DISK_GEOMETRY G; + + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_DISK_GET_DRIVE_GEOMETRY, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &G, /* output buffer */ + (DWORD) sizeof(G), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + if (sector_size) + *sector_size = G.BytesPerSector; + } +#endif +#ifdef IOCTL_STORAGE_GET_HOTPLUG_INFO +if (1) { + STORAGE_HOTPLUG_INFO H; + + ZeroMemory (&H, sizeof (H)); + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_STORAGE_GET_HOTPLUG_INFO, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &H, /* output buffer */ + (DWORD) sizeof(H), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + if (removable) + *removable = H.MediaRemovable; + } +#endif +if (removable && *removable) + printf ("Removable Device\n"); +return SCPE_OK; +} + +static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +OVERLAPPED pos; +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; +long long addr; + +sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", uptr-ctx->dptr->units, lba, sects); + +addr = ((long long)lba) * ctx->sector_size; +memset (&pos, 0, sizeof (pos)); +pos.Offset = (DWORD)addr; +pos.OffsetHigh = (DWORD)(addr >> 32); +if (ReadFile ((HANDLE)(uptr->fileref), buf, sects * ctx->sector_size, (LPDWORD)sectsread, &pos)) { + if (sectsread) + *sectsread /= ctx->sector_size; + return SCPE_OK; + } +_set_errno_from_status (GetLastError ()); +return SCPE_IOERR; +} + +static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +OVERLAPPED pos; +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; +long long addr; + +sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", uptr-ctx->dptr->units, lba, sects); + +addr = ((long long)lba) * ctx->sector_size; +memset (&pos, 0, sizeof (pos)); +pos.Offset = (DWORD)addr; +pos.OffsetHigh = (DWORD)(addr >> 32); +if (WriteFile ((HANDLE)(uptr->fileref), buf, sects * ctx->sector_size, (LPDWORD)sectswritten, &pos)) { + if (sectswritten) + *sectswritten /= ctx->sector_size; + return SCPE_OK; + } +_set_errno_from_status (GetLastError ()); +return SCPE_IOERR; +} + +#elif defined (__linux) || defined (__sun__) + +#include +#include +#include +#include + +static t_stat sim_os_disk_implemented_raw (void) +{ +return SCPE_OK; +} + +static FILE *sim_os_disk_open_raw (const char *rawdevicename, const char *openmode) +{ +int fd; +int mode = 0; + +if (strchr (openmode, 'r') && (strchr (openmode, '+') || strchr (openmode, 'w'))) + mode = O_RDWR; +else + if (strchr (openmode, 'r')) + mode = O_RDONLY; +#ifdef O_LARGEFILE +mode |= O_LARGEFILE; +#endif +return (FILE *)((long)open (rawdevicename, mode, 0)); +} + +static int sim_os_disk_close_raw (FILE *f) +{ +return close ((int)((long)f)); +} + +static void sim_os_disk_flush_raw (FILE *f) +{ +fsync ((int)((long)f)); +} + +static t_addr sim_os_disk_size_raw (FILE *f) +{ +struct stat64 statb; + +if (fstat64 ((int)((long)f), &statb)) + return (t_addr)-1; +return (t_addr)statb.st_size; +} + +static t_stat sim_os_disk_unload_raw (FILE *f) +{ +return SCPE_IOERR; +} + +static t_bool sim_os_disk_isavailable_raw (FILE *Disk) +{ +return TRUE; +} + +static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; +off_t addr; +ssize_t bytesread; + +sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", uptr-ctx->dptr->units, lba, sects); + +addr = ((off_t)lba) * ctx->sector_size; +bytesread = pread((int)((long)uptr->fileref), buf, sects * ctx->sector_size, addr); +if (bytesread < 0) { + if (sectsread) + *sectsread = 0; + return SCPE_IOERR; + } +if (sectsread) + *sectsread = bytesread / ctx->sector_size; +return SCPE_OK; +} + +static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; +off_t addr; +ssize_t byteswritten; + +sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", uptr-ctx->dptr->units, lba, sects); + +addr = ((off_t)lba) * ctx->sector_size; +byteswritten = pwrite((int)((long)uptr->fileref), buf, sects * ctx->sector_size, addr); +if (byteswritten < 0) { + if (sectswritten) + *sectswritten = 0; + return SCPE_IOERR; + } +if (sectswritten) + *sectswritten = byteswritten / ctx->sector_size; +return SCPE_OK; +} + +static t_stat sim_os_disk_info_raw (FILE *f, uint32 *sector_size, uint32 *removable) +{ +if (sector_size) + *sector_size = 512; +if (removable) + *removable = 0; +return SCPE_OK; +} + +#else +/*============================================================================*/ +/* Non-implemented versions */ +/*============================================================================*/ + +static t_stat sim_os_disk_implemented_raw (void) +{ +return SCPE_NOFNC; +} + +static FILE *sim_os_disk_open_raw (const char *rawdevicename, const char *openmode) +{ +return NULL; +} + +static int sim_os_disk_close_raw (FILE *f) +{ +return EOF; +} + +static void sim_os_disk_flush_raw (FILE *f) +{ +} + +static t_addr sim_os_disk_size_raw (FILE *f) +{ +return (t_addr)-1; +} + +static t_stat sim_os_disk_unload_raw (FILE *f) +{ +return SCPE_NOFNC; +} + +static t_bool sim_os_disk_isavailable_raw (FILE *Disk) +{ +return FALSE; +} + +static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +return SCPE_NOFNC; +} + +static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +return SCPE_NOFNC; +} + +static t_stat sim_os_disk_info_raw (FILE *f, uint32 *sector_size, uint32 *removable) +{ +return SCPE_NOFNC; +} + +#endif + +/* OS Independent Disk Virtual Disk (VHD) I/O support */ + +#if (defined (VMS) && !(defined (__ALPHA) || defined (__ia64))) +#define DONT_DO_VHD_SUPPORT /* VAX/VMS compilers don't have 64 bit integers */ +#endif + +#if defined (DONT_DO_VHD_SUPPORT) + +/*============================================================================*/ +/* Non-implemented version */ +/* This is only for hody systems which don't have 64 bit integer types */ +/*============================================================================*/ + +static t_stat sim_vhd_disk_implemented (void) +{ +return SCPE_NOFNC; +} + +static FILE *sim_vhd_disk_open (const char *rawdevicename, const char *openmode) +{ +return NULL; +} + +static FILE *sim_vhd_disk_create (const char *szVHDPath, t_addr desiredsize) +{ +return NULL; +} + +static FILE *sim_vhd_disk_create_diff (const char *szVHDPath, const char *szParentVHDPath) +{ +return NULL; +} + +static int sim_vhd_disk_close (FILE *f) +{ +return -1; +} + +static void sim_vhd_disk_flush (FILE *f) +{ +} + +static t_addr sim_vhd_disk_size (FILE *f) +{ +return (t_addr)-1; +} + +static t_stat sim_vhd_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +return SCPE_IOERR; +} + +static t_stat sim_vhd_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +return SCPE_IOERR; +} + +static t_stat sim_vhd_disk_set_dtype (FILE *f, const char *dtype) +{ +return SCPE_NOFNC; +} + +static const char *sim_vhd_disk_get_dtype (FILE *f) +{ +return NULL; +} + +#else + +/*++ + This code follows the details specified in the "Virtual Hard Disk Image + Format Specification", Version 1.0 October 11, 2006. +--*/ + +typedef t_uint64 uint64; +typedef t_int64 int64; + +typedef struct _VHD_Footer { + /* + Cookies are used to uniquely identify the original creator of the hard disk + image. The values are case-sensitive. Microsoft uses the “conectix” string + to identify this file as a hard disk image created by Microsoft Virtual + Server, Virtual PC, and predecessor products. The cookie is stored as an + eight-character ASCII string with the “c” in the first byte, the “o” in + the second byte, and so on. + */ + char Cookie[8]; + /* + This is a bit field used to indicate specific feature support. The following + table displays the list of features. + Any fields not listed are reserved. + + Feature Value: + No features enabled 0x00000000 + Temporary 0x00000001 + Reserved 0x00000002 + + No features enabled. + The hard disk image has no special features enabled in it. + Temporary. + This bit is set if the current disk is a temporary disk. A + temporary disk designation indicates to an application that + this disk is a candidate for deletion on shutdown. + Reserved. + This bit must always be set to 1. + All other bits are also reserved and should be set to 0. + */ + uint32 Features; + /* + This field is divided into a major/minor version and matches the version of + the specification used in creating the file. The most-significant two bytes + are for the major version. The least-significant two bytes are the minor + version. This must match the file format specification. For the current + specification, this field must be initialized to 0x00010000. + The major version will be incremented only when the file format is modified + in such a way that it is no longer compatible with older versions of the + file format. + */ + uint32 FileFormatVersion; + /* + This field holds the absolute byte offset, from the beginning of the file, + to the next structure. This field is used for dynamic disks and differencing + disks, but not fixed disks. For fixed disks, this field should be set to + 0xFFFFFFFF. + */ + uint64 DataOffset; + /* + This field stores the creation time of a hard disk image. This is the number + of seconds since January 1, 2000 12:00:00 AM in UTC/GMT. + */ + uint32 TimeStamp; + /* + This field is used to document which application created the hard disk. The + field is a left-justified text field. It uses a single-byte character set. + If the hard disk is created by Microsoft Virtual PC, "vpc " is written in + this field. If the hard disk image is created by Microsoft Virtual Server, + then "vs " is written in this field. + Other applications should use their own unique identifiers. + */ + char CreatorApplication[4]; + /* + This field holds the major/minor version of the application that created + the hard disk image. Virtual Server 2004 sets this value to 0x00010000 and + Virtual PC 2004 sets this to 0x00050000. + */ + uint32 CreatorVersion; + /* + This field stores the type of host operating system this disk image is + created on. + Host OS type Value + Windows 0x5769326B (Wi2k) + Macintosh 0x4D616320 (Mac ) + */ + uint8 CreatorHostOS[4]; + /* + This field stores the size of the hard disk in bytes, from the perspective + of the virtual machine, at creation time. This field is for informational + purposes. + */ + uint64 OriginalSize; + /* + This field stores the current size of the hard disk, in bytes, from the + perspective of the virtual machine. + This value is same as the original size when the hard disk is created. + This value can change depending on whether the hard disk is expanded. + */ + uint64 CurrentSize; + /* + This field stores the cylinder, heads, and sectors per track value for the + hard disk. + Disk Geometry field Size (bytes) + Cylinder 2 + Heads 1 + Sectors per track/cylinder 1 + + When a hard disk is configured as an ATA hard disk, the CHS values (that is, + Cylinder, Heads, Sectors per track) are used by the ATA controller to + determine the size of the disk. When the user creates a hard disk of a + certain size, the size of the hard disk image in the virtual machine is + smaller than that created by the user. This is because CHS value calculated + from the hard disk size is rounded down. The pseudo-code for the algorithm + used to determine the CHS values can be found in the appendix of this + document. + */ + uint32 DiskGeometry; + /* + Disk Type field Value + None 0 + Reserved (deprecated) 1 + Fixed hard disk 2 + Dynamic hard disk 3 + Differencing hard disk 4 + Reserved (deprecated) 5 + Reserved (deprecated) 6 + */ + uint32 DiskType; + /* + This field holds a basic checksum of the hard disk footer. It is just a + one’s complement of the sum of all the bytes in the footer without the + checksum field. + If the checksum verification fails, the Virtual PC and Virtual Server + products will instead use the header. If the checksum in the header also + fails, the file should be assumed to be corrupt. The pseudo-code for the + algorithm used to determine the checksum can be found in the appendix of + this document. + */ + uint32 Checksum; + /* + Every hard disk has a unique ID stored in the hard disk. This is used to + identify the hard disk. This is a 128-bit universally unique identifier + (UUID). This field is used to associate a parent hard disk image with its + differencing hard disk image(s). + */ + uint8 UniqueID[16]; + /* + This field holds a one-byte flag that describes whether the system is in + saved state. If the hard disk is in the saved state the value is set to 1. + Operations such as compaction and expansion cannot be performed on a hard + disk in a saved state. + */ + uint8 SavedState; + /* + This field contains zeroes. It is 427 bytes in size. + */ + uint8 Reserved1[11]; + /* + This field is an extension to the VHD spec and includes a simh drive type + name as a nul terminated string. + */ + uint8 DriveType[16]; + /* + This field contains zeroes. It is 400 bytes in size. + */ + uint8 Reserved[400]; + } VHD_Footer; + +/* +For dynamic and differencing disk images, the “Data Offset” field within +the image footer points to a secondary structure that provides additional +information about the disk image. The dynamic disk header should appear on +a sector (512-byte) boundary. +*/ +typedef struct _VHD_DynamicDiskHeader { + /* + This field holds the value "cxsparse". This field identifies the header. + */ + char Cookie[8]; + /* + This field contains the absolute byte offset to the next structure in the + hard disk image. It is currently unused by existing formats and should be + set to 0xFFFFFFFF. + */ + uint64 DataOffset; + /* + This field stores the absolute byte offset of the Block Allocation Table + (BAT) in the file. + */ + uint64 TableOffset; + /* + This field stores the version of the dynamic disk header. The field is + divided into Major/Minor version. The least-significant two bytes represent + the minor version, and the most-significant two bytes represent the major + version. This must match with the file format specification. For this + specification, this field must be initialized to 0x00010000. + The major version will be incremented only when the header format is + modified in such a way that it is no longer compatible with older versions + of the product. + */ + uint32 HeaderVersion; + /* + This field holds the maximum entries present in the BAT. This should be + equal to the number of blocks in the disk (that is, the disk size divided + by the block size). + */ + uint32 MaxTableEntries; + /* + A block is a unit of expansion for dynamic and differencing hard disks. It + is stored in bytes. This size does not include the size of the block bitmap. + It is only the size of the data section of the block. The sectors per block + must always be a power of two. The default value is 0x00200000 (indicating a + block size of 2 MB). + */ + uint32 BlockSize; + /* + This field holds a basic checksum of the dynamic header. It is a one’s + complement of the sum of all the bytes in the header without the checksum + field. + If the checksum verification fails the file should be assumed to be corrupt. + */ + uint32 Checksum; + /* + This field is used for differencing hard disks. A differencing hard disk + stores a 128-bit UUID of the parent hard disk. For more information, see + “Creating Differencing Hard Disk Images” later in this paper. + */ + uint8 ParentUniqueID[16]; + /* + This field stores the modification time stamp of the parent hard disk. This + is the number of seconds since January 1, 2000 12:00:00 AM in UTC/GMT. + */ + uint32 ParentTimeStamp; + /* + This field should be set to zero. + */ + uint32 Reserved0; + /* + This field contains a Unicode string (UTF-16) of the parent hard disk + filename. + */ + char ParentUnicodeName[512]; + /* + These entries store an absolute byte offset in the file where the parent + locator for a differencing hard disk is stored. This field is used only for + differencing disks and should be set to zero for dynamic disks. + */ + struct VHD_ParentLocator { + /* + The platform code describes which platform-specific format is used for the + file locator. For Windows, a file locator is stored as a path (for example. + “c:\disksimages\ParentDisk.vhd”). On a Macintosh system, the file locator + is a binary large object (blob) that contains an “alias.” The parent locator + table is used to support moving hard disk images across platforms. + Some current platform codes include the following: + Platform Code Description + None (0x0) + Wi2r (0x57693272) [deprecated] + Wi2k (0x5769326B) [deprecated] + W2ru (0x57327275) Unicode pathname (UTF-16) on Windows relative to the differencing disk pathname. + W2ku (0x57326B75) Absolute Unicode (UTF-16) pathname on Windows. + Mac (0x4D616320) (Mac OS alias stored as a blob) + MacX(0x4D616358) A file URL with UTF-8 encoding conforming to RFC 2396. + */ + uint8 PlatformCode[4]; + /* + This field stores the number of 512-byte sectors needed to store the parent + hard disk locator. + */ + uint32 PlatformDataSpace; + /* + This field stores the actual length of the parent hard disk locator in bytes. + */ + uint32 PlatformDataLength; + /* + This field must be set to zero. + */ + uint32 Reserved; + /* + This field stores the absolute file offset in bytes where the platform + specific file locator data is stored. + */ + uint64 PlatformDataOffset; + /* + This field stores the absolute file offset in bytes where the platform + specific file locator data is stored. + */ + } ParentLocatorEntries[8]; + /* + This must be initialized to zeroes. + */ + char Reserved[256]; + } VHD_DynamicDiskHeader; + +#define VHD_BAT_FREE_ENTRY (0xFFFFFFFF) + +static char *VHD_DiskTypes[] = + { + "None", /* 0 */ + "Reserved (deprecated)", /* 1 */ + "Fixed hard disk", /* 2 */ +#define VHD_DT_Fixed 2 + "Dynamic hard disk", /* 3 */ +#define VHD_DT_Dynamic 3 + "Differencing hard disk", /* 4 */ +#define VHD_DT_Differencing 4 + "Reserved (deprecated)", /* 5 */ + "Reserved (deprecated)", /* 6 */ + }; + +static uint32 NtoHl(uint32 value); + +static uint64 NtoHll(uint64 value); + +typedef struct VHD_IOData *VHDHANDLE; + +static t_stat ReadFilePosition(FILE *File, void *buf, size_t bufsize, size_t *bytesread, uint64 position) +{ +uint32 err = sim_fseek (File, (t_addr)position, SEEK_SET); +size_t i; + +if (!err) { + i = fread (buf, 1, bufsize, File); + err = ferror (File); + if ((!err) && bytesread) + *bytesread = i; + } +return (err ? SCPE_IOERR : SCPE_OK); +} + +static t_stat WriteFilePosition(FILE *File, void *buf, size_t bufsize, size_t *byteswritten, uint64 position) +{ +uint32 err = sim_fseek (File, (t_addr)position, SEEK_SET); +size_t i; + +if (!err) { + i = fwrite (buf, 1, bufsize, File); + err = ferror (File); + if ((!err) && byteswritten) + *byteswritten = i; + } +return (err ? SCPE_IOERR : SCPE_OK); +} + +static uint32 +CalculateVhdFooterChecksum(void *data, + size_t size) +{ +uint32 sum = 0; +uint8 *c = (uint8 *)data; + +while (size--) + sum += *c++; +return ~sum; +} + +#if defined(_WIN32) || defined (__ALPHA) || defined (__ia64) || defined (VMS) +#ifndef __BYTE_ORDER__ +#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ +#endif +#endif +#ifndef __BYTE_ORDER__ +#define __BYTE_ORDER__ UNKNOWN +#endif +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +static uint32 +NtoHl(uint32 value) +{ +uint8 *l = (uint8 *)&value; +return l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24); +} + +static uint64 +NtoHll(uint64 value) +{ +uint8 *l = (uint8 *)&value; +uint64 highresult = l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24); +uint32 lowresult = l[7] | (l[6]<<8) | (l[5]<<16) | (l[4]<<24); +return (highresult << 32) | lowresult; +} +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +static uint32 +NtoHl(uint32 value) +{ +return value; +} + +static uint64 +NtoHll(uint64 value) +{ +return value; +} +#else +static uint32 +NtoHl(uint32 value) +{ +uint8 *l = (uint8 *)&value; + +if (sim_end) + return l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24); +return value; +} + +static uint64 +NtoHll(uint64 value) +{ +uint8 *l = (uint8 *)&value; + +if (sim_end) { + uint64 highresult = l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24); + uint32 lowresult = l[7] | (l[6]<<8) | (l[5]<<16) | (l[4]<<24); + return (highresult << 32) | lowresult; + } +return value; +} +#endif + +static +int +GetVHDFooter(const char *szVHDPath, + VHD_Footer *sFooter, + VHD_DynamicDiskHeader *sDynamic, + uint32 **aBAT, + uint32 *ModifiedTimeStamp, + char *szParentVHDPath, + size_t ParentVHDPathSize) +{ +FILE *File = NULL; +uint64 position; +uint32 sum, saved_sum; +int Return = 0; +VHD_Footer sHeader; +struct stat statb; + +if (sFooter) + memset(sFooter, '\0', sizeof(*sFooter)); +if (sDynamic) + memset(sDynamic, '\0', sizeof(*sDynamic)); +if (aBAT) + *aBAT = NULL; +File = sim_fopen (szVHDPath, "rb"); +if (!File) { + Return = errno; + goto Return_Cleanup; + } +if (ModifiedTimeStamp) + if (stat (szVHDPath, &statb)) { + Return = errno; + goto Return_Cleanup; + } + else + *ModifiedTimeStamp = NtoHl ((uint32)(statb.st_mtime-946684800)); +position = sim_fsize_ex (File); +if (((int64)position) == -1) { + Return = errno; + goto Return_Cleanup; + } +position -= sizeof(*sFooter); +if (ReadFilePosition(File, + sFooter, + sizeof(*sFooter), + NULL, + position)) { + Return = errno; + goto Return_Cleanup; + } +saved_sum = NtoHl(sFooter->Checksum); +sFooter->Checksum = 0; +sum = CalculateVhdFooterChecksum(sFooter, sizeof(*sFooter)); +sFooter->Checksum = NtoHl(saved_sum); +if ((sum != saved_sum) || (memcmp("conectix", sFooter->Cookie, sizeof(sFooter->Cookie)))) { + Return = EINVAL; /* File Corrupt */ + goto Return_Cleanup; + } +if (ReadFilePosition(File, + &sHeader, + sizeof(sHeader), + NULL, + (uint64)0)) { + Return = errno; + goto Return_Cleanup; + } +if ((NtoHl(sFooter->DiskType) != VHD_DT_Dynamic) && + (NtoHl(sFooter->DiskType) != VHD_DT_Differencing) && + (NtoHl(sFooter->DiskType) != VHD_DT_Fixed)) { + Return = EINVAL; /* File Corrupt */ + goto Return_Cleanup; + } +if (((NtoHl(sFooter->DiskType) == VHD_DT_Dynamic) || + (NtoHl(sFooter->DiskType) == VHD_DT_Differencing)) && + memcmp(sFooter, &sHeader, sizeof(sHeader))) { + Return = EINVAL; /* File Corrupt */ + goto Return_Cleanup; + } +if ((sDynamic) && + ((NtoHl(sFooter->DiskType) == VHD_DT_Dynamic) || + (NtoHl(sFooter->DiskType) == VHD_DT_Differencing))) { + if (ReadFilePosition(File, + sDynamic, + sizeof (*sDynamic), + NULL, + NtoHll (sFooter->DataOffset))) { + Return = errno; + goto Return_Cleanup; + } + saved_sum = NtoHl (sDynamic->Checksum); + sDynamic->Checksum = 0; + sum = CalculateVhdFooterChecksum (sDynamic, sizeof(*sDynamic)); + sDynamic->Checksum = NtoHl (saved_sum); + if ((sum != saved_sum) || (memcmp ("cxsparse", sDynamic->Cookie, sizeof (sDynamic->Cookie)))) { + Return = errno; + goto Return_Cleanup; + } + if (aBAT) + { + *aBAT = malloc(512*((sizeof(**aBAT)*NtoHl(sDynamic->MaxTableEntries)+511)/512)); + if (ReadFilePosition(File, + *aBAT, + sizeof (**aBAT)*NtoHl(sDynamic->MaxTableEntries), + NULL, + NtoHll (sDynamic->TableOffset))) { + Return = EINVAL; /* File Corrupt */ + goto Return_Cleanup; + } + } + if (szParentVHDPath && ParentVHDPathSize) { + VHD_Footer sParentFooter; + + memset (szParentVHDPath, '\0', ParentVHDPathSize); + if (NtoHl (sFooter->DiskType) == VHD_DT_Differencing) + { + size_t i, j; + + for (j=0; j<8; ++j) + { + uint8 *Pdata; + char ParentName[256]; + char CheckPath[256]; + uint32 ParentModificationTime; + + if ('\0' == sDynamic->ParentLocatorEntries[j].PlatformCode[0]) + continue; + memset (ParentName, '\0', sizeof(ParentName)); + memset (CheckPath, '\0', sizeof(CheckPath)); + Pdata = calloc (1, NtoHl(sDynamic->ParentLocatorEntries[j].PlatformDataSpace)+1); + if (!Pdata) + continue; + if (ReadFilePosition(File, + Pdata, + NtoHl (sDynamic->ParentLocatorEntries[j].PlatformDataSpace), + NULL, + NtoHll (sDynamic->ParentLocatorEntries[j].PlatformDataOffset))) { + free (Pdata); + continue; + } + for (i=0; iParentLocatorEntries[j].PlatformDataLength); i+=2) + if ((Pdata[i] == '\0') && (Pdata[i+1] == '\0')) { + ParentName[i/2] = '\0'; + break; + } + else + ParentName[i/2] = Pdata[i] ? Pdata[i] : Pdata[i+1]; + free (Pdata); + if (0 == memcmp (sDynamic->ParentLocatorEntries[j].PlatformCode, "W2ku", 4)) + strncpy (CheckPath, ParentName, sizeof (CheckPath)-1); + else + if (0 == memcmp (sDynamic->ParentLocatorEntries[j].PlatformCode, "W2ru", 4)) { + char *c; + + if ((c = strrchr (szVHDPath, '/')) || (c = strrchr (szVHDPath, '\\'))) + memcpy (CheckPath, szVHDPath, c-szVHDPath+1); + strncpy (CheckPath+strlen(CheckPath), ParentName, sizeof (CheckPath)-(strlen (CheckPath)+1)); + } + if ((0 == GetVHDFooter(CheckPath, + &sParentFooter, + NULL, + NULL, + &ParentModificationTime, + NULL, + 0)) && + (0 == memcmp (sDynamic->ParentUniqueID, sParentFooter.UniqueID, sizeof (sParentFooter.UniqueID))) && + (sDynamic->ParentTimeStamp == ParentModificationTime)) + { + strncpy (szParentVHDPath, CheckPath, ParentVHDPathSize); + break; + } + } + if (!szParentVHDPath) + Return = EINVAL; /* File Corrupt */ + } + } + } +Return_Cleanup: +if (File) + fclose(File); +if (aBAT && (0 != Return)) { + free (*aBAT); + *aBAT = NULL; + } +return errno = Return; +} + +struct VHD_IOData { + VHD_Footer Footer; + VHD_DynamicDiskHeader Dynamic; + uint32 *BAT; + FILE *File; + char ParentVHDPath[512]; + struct VHD_IOData *Parent; + }; + +static t_stat sim_vhd_disk_implemented (void) +{ +return SCPE_OK; +} + +static t_stat sim_vhd_disk_set_dtype (FILE *f, const char *dtype) +{ +VHDHANDLE hVHD = (VHDHANDLE)f; +int Status = 0; + +memset (hVHD->Footer.DriveType, '\0', sizeof hVHD->Footer.DriveType); +memcpy (hVHD->Footer.DriveType, dtype, ((1+strlen (dtype)) < sizeof (hVHD->Footer.DriveType)) ? (1+strlen (dtype)) : sizeof (hVHD->Footer.DriveType)); +hVHD->Footer.Checksum = 0; +hVHD->Footer.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Footer, sizeof(hVHD->Footer))); + +if (NtoHl (hVHD->Footer.DiskType) == VHD_DT_Fixed) { + if (WriteFilePosition(hVHD->File, + &hVHD->Footer, + sizeof(hVHD->Footer), + NULL, + NtoHll (hVHD->Footer.CurrentSize))) + Status = errno; + goto Cleanup_Return; + } +else { + uint64 position; + + position = sim_fsize_ex (hVHD->File); + if (((int64)position) == -1) { + Status = errno; + goto Cleanup_Return; + } + position -= sizeof(hVHD->Footer); + /* Update both copies on a dynamic disk */ + if (WriteFilePosition(hVHD->File, + &hVHD->Footer, + sizeof(hVHD->Footer), + NULL, + (uint64)0)) { + Status = errno; + goto Cleanup_Return; + } + if (WriteFilePosition(hVHD->File, + &hVHD->Footer, + sizeof(hVHD->Footer), + NULL, + position)) { + Status = errno; + goto Cleanup_Return; + } + } +Cleanup_Return: +if (Status) + return SCPE_IOERR; +return SCPE_OK; +} + +static const char *sim_vhd_disk_get_dtype (FILE *f) +{ +VHDHANDLE hVHD = (VHDHANDLE)f; + +return (char *)(&hVHD->Footer.DriveType[0]); +} + +static FILE *sim_vhd_disk_open (const char *szVHDPath, const char *DesiredAccess) + { + VHDHANDLE hVHD = calloc (1, sizeof(*hVHD)); + int Status; + + if (!hVHD) + return (FILE *)hVHD; + if (Status = GetVHDFooter (szVHDPath, + &hVHD->Footer, + &hVHD->Dynamic, + &hVHD->BAT, + NULL, + hVHD->ParentVHDPath, + sizeof (hVHD->ParentVHDPath))) + goto Cleanup_Return; + if (NtoHl (hVHD->Footer.DiskType) == VHD_DT_Differencing) { + hVHD->Parent = (VHDHANDLE)sim_vhd_disk_open (hVHD->ParentVHDPath, "rb"); + if (!hVHD->Parent) { + Status = errno; + goto Cleanup_Return; + } + } + if (hVHD->Footer.SavedState) { + Status = EAGAIN; /* Busy */ + goto Cleanup_Return; + } + hVHD->File = sim_fopen (szVHDPath, DesiredAccess); + if (!hVHD->File) { + Status = errno; + goto Cleanup_Return; + } +Cleanup_Return: + if (Status) { + free (hVHD->BAT); + free (hVHD); + hVHD = NULL; + } + errno = Status; + return (FILE *)hVHD; + } + +static int sim_vhd_disk_close (FILE *f) +{ +VHDHANDLE hVHD = (VHDHANDLE)f; + +if (NULL != hVHD) { + if (hVHD->Parent) + sim_vhd_disk_close ((FILE *)hVHD->Parent); + free (hVHD->BAT); + if (hVHD->File) { + fflush (hVHD->File); + fclose (hVHD->File); + } + free (hVHD); + return 0; + } +return -1; +} + +static void sim_vhd_disk_flush (FILE *f) +{ +VHDHANDLE hVHD = (VHDHANDLE)f; + +if ((NULL != hVHD) && (hVHD->File)) + fflush (hVHD->File); +} + +static t_addr sim_vhd_disk_size (FILE *f) +{ +VHDHANDLE hVHD = (VHDHANDLE)f; + +return (t_addr)(NtoHll (hVHD->Footer.CurrentSize)); +} + +#include +#include +static void +_rand_uuid_gen (void *uuidaddr) +{ +int i; +uint8 *b = (uint8 *)uuidaddr; +uint32 timenow = (uint32)time (NULL); + +memcpy (uuidaddr, &timenow, sizeof (timenow)); +srand ((unsigned)timenow); +for (i=4; i<16; i++) { + b[i] = (uint8)rand(); + } +} + +#if defined (_WIN32) +static void +uuid_gen (void *uuidaddr) +{ +static +RPC_STATUS +(RPC_ENTRY *UuidCreate_c) (void *); + +if (!UuidCreate_c) { + HINSTANCE hDll; + hDll = LoadLibrary("rpcrt4.dll"); + UuidCreate_c = (void *)GetProcAddress(hDll, "UuidCreate"); + } +if (UuidCreate_c) + UuidCreate_c(uuidaddr); +else + _rand_uuid_gen (uuidaddr); +} +#elif defined (USE_LIBUUID) +#include + +static void +uuid_gen (void *uuidaddr) +{ +uuid_generate((uuid_t)uuidaddr); +} +#else +static void +uuid_gen (void *uuidaddr) +{ +_rand_uuid_gen (uuidaddr); +} +#endif + +static VHDHANDLE +CreateVirtualDisk(const char *szVHDPath, + uint32 SizeInSectors, + uint32 BlockSize, + t_bool bFixedVHD) +{ +VHD_Footer Footer; +VHD_DynamicDiskHeader Dynamic; +uint32 *BAT = NULL; +time_t now; +uint32 i; +FILE *File; +uint32 Status = 0; +uint32 BytesPerSector = 512; +uint64 SizeInBytes = ((uint64)SizeInSectors)*BytesPerSector; +uint32 MaxTableEntries; +VHDHANDLE hVHD = NULL; + +if (SizeInBytes > ((uint64)(1024*1024*1024))*2040) { + Status = EFBIG; + goto Cleanup_Return; + } +if (File = sim_fopen (szVHDPath, "rb")) { + fclose (File); + Status = EEXIST; + goto Cleanup_Return; + } +File = sim_fopen (szVHDPath, "wb"); +if (!File) { + Status = errno; + goto Cleanup_Return; + } + +memset (&Footer, 0, sizeof(Footer)); +memcpy (Footer.Cookie, "conectix", 8); +Footer.Features = NtoHl (0x00000002);; +Footer.FileFormatVersion = NtoHl (0x00010000);; +Footer.DataOffset = NtoHll (bFixedVHD ? ((long long)-1) : (long long)(sizeof(Footer))); +time (&now); +Footer.TimeStamp = NtoHl ((uint32)(now-946684800)); +memcpy (Footer.CreatorApplication, "simh", 4); +Footer.CreatorVersion = NtoHl (0x00010000); +memcpy (Footer.CreatorHostOS, "Wi2k", 4); +Footer.OriginalSize = NtoHll (SizeInBytes); +Footer.CurrentSize = NtoHll (SizeInBytes); +uuid_gen (Footer.UniqueID); +Footer.DiskType = NtoHl (bFixedVHD ? VHD_DT_Fixed : VHD_DT_Dynamic); +Footer.DiskGeometry = NtoHl (0xFFFF10FF); +if (1) { /* CHS Calculation */ + uint32 totalSectors = (uint32)(SizeInBytes/BytesPerSector);/* Total data sectors present in the disk image */ + uint32 cylinders; /* Number of cylinders present on the disk */ + uint32 heads; /* Number of heads present on the disk */ + uint32 sectorsPerTrack; /* Sectors per track on the disk */ + uint32 cylinderTimesHeads; /* Cylinders x heads */ + + if (totalSectors > 65535 * 16 * 255) + totalSectors = 65535 * 16 * 255; + + if (totalSectors >= 65535 * 16 * 63) { + sectorsPerTrack = 255; + heads = 16; + cylinderTimesHeads = totalSectors / sectorsPerTrack; + } + else { + sectorsPerTrack = 17; + cylinderTimesHeads = totalSectors / sectorsPerTrack; + + heads = (cylinderTimesHeads + 1023) / 1024; + + if (heads < 4) + heads = 4; + if (cylinderTimesHeads >= (heads * 1024) || heads > 16) + { + sectorsPerTrack = 31; + heads = 16; + cylinderTimesHeads = totalSectors / sectorsPerTrack; + } + if (cylinderTimesHeads >= (heads * 1024)) + { + sectorsPerTrack = 63; + heads = 16; + cylinderTimesHeads = totalSectors / sectorsPerTrack; + } + } + cylinders = cylinderTimesHeads / heads; + Footer.DiskGeometry = NtoHl ((cylinders<<16)|(heads<<8)|sectorsPerTrack); + } +Footer.Checksum = NtoHl (CalculateVhdFooterChecksum(&Footer, sizeof(Footer))); + +if (bFixedVHD) { + if (WriteFilePosition(File, + &Footer, + sizeof(Footer), + NULL, + SizeInBytes)) + Status = errno; + goto Cleanup_Return; + } + +/* Dynamic Disk */ +memset (&Dynamic, 0, sizeof(Dynamic)); +memcpy (Dynamic.Cookie, "cxsparse", 8); +Dynamic.DataOffset = NtoHll (0xFFFFFFFFFFFFFFFFLL); +Dynamic.TableOffset = NtoHll ((uint64)(BytesPerSector*((sizeof(Dynamic)+sizeof(Footer)+BytesPerSector-1)/BytesPerSector))); +Dynamic.HeaderVersion = NtoHl (0x00010000); +if (0 == BlockSize) + BlockSize = 2*1024*1024; +Dynamic.BlockSize = NtoHl (BlockSize); +MaxTableEntries = (uint32)((SizeInBytes+BlockSize-1)/BlockSize); +Dynamic.MaxTableEntries = NtoHl (MaxTableEntries); +Dynamic.Checksum = NtoHl (CalculateVhdFooterChecksum(&Dynamic, sizeof(Dynamic))); +BAT = malloc (BytesPerSector*((MaxTableEntries*sizeof(*BAT)+BytesPerSector-1)/BytesPerSector)); +memset (BAT, 0, BytesPerSector*((MaxTableEntries*sizeof(*BAT)+BytesPerSector-1)/BytesPerSector)); +for (i=0; iDynamic.TableOffset)+BytesPerSector*((NtoHl (hVHD->Dynamic.MaxTableEntries)*sizeof(*hVHD->BAT)+BytesPerSector-1)/BytesPerSector); +hVHD->Dynamic.Checksum = 0; +RelativeParentVHDPath = calloc (1, BytesPerSector+1); +FullParentVHDPath = calloc (1, BytesPerSector+1); +RelativeParentVHDPathBuffer = calloc (1, BytesPerSector); +FullParentVHDPathBuffer = calloc (1, BytesPerSector); +FullVHDPath = calloc (1, BytesPerSector+1); +ExpandToFullPath (szParentVHDPath, FullParentVHDPath, BytesPerSector); +for (i=0; i < strlen (FullParentVHDPath); i++) + hVHD->Dynamic.ParentUnicodeName[i*2+1] = FullParentVHDPath[i]; +for (i=0; i < strlen (FullParentVHDPath); i++) + FullParentVHDPathBuffer[i*2] = FullParentVHDPath[i]; +ExpandToFullPath (szVHDPath, FullVHDPath, BytesPerSector); +for (i=0, RelativeMatch=UpDirectories=0; iDynamic.ParentTimeStamp = ParentTimeStamp; +memcpy (hVHD->Dynamic.ParentUniqueID, ParentFooter.UniqueID, sizeof (hVHD->Dynamic.ParentUniqueID)); +hVHD->Dynamic.ParentLocatorEntries[7].PlatformDataSpace = NtoHl (BytesPerSector); +hVHD->Dynamic.ParentLocatorEntries[7].PlatformDataOffset = NtoHll (LocatorPosition+LocatorsWritten*BytesPerSector); +++LocatorsWritten; +hVHD->Dynamic.ParentLocatorEntries[6].PlatformDataSpace = NtoHl (BytesPerSector); +hVHD->Dynamic.ParentLocatorEntries[6].PlatformDataOffset = NtoHll (LocatorPosition+LocatorsWritten*BytesPerSector); +++LocatorsWritten; +if (RelativeMatch) { + memcpy (hVHD->Dynamic.ParentLocatorEntries[5].PlatformCode, "W2ru", 4); + hVHD->Dynamic.ParentLocatorEntries[5].PlatformDataSpace = NtoHl (BytesPerSector); + hVHD->Dynamic.ParentLocatorEntries[5].PlatformDataLength = NtoHl (2*strlen(RelativeParentVHDPath)); + hVHD->Dynamic.ParentLocatorEntries[5].Reserved = 0; + hVHD->Dynamic.ParentLocatorEntries[5].PlatformDataOffset = NtoHll (LocatorPosition+LocatorsWritten*BytesPerSector); + ++LocatorsWritten; + } +memcpy (hVHD->Dynamic.ParentLocatorEntries[4].PlatformCode, "W2ku", 4); +hVHD->Dynamic.ParentLocatorEntries[4].PlatformDataSpace = NtoHl (BytesPerSector); +hVHD->Dynamic.ParentLocatorEntries[4].PlatformDataLength = NtoHl (2*strlen(FullParentVHDPath)); +hVHD->Dynamic.ParentLocatorEntries[4].Reserved = 0; +hVHD->Dynamic.ParentLocatorEntries[4].PlatformDataOffset = NtoHll (LocatorPosition+LocatorsWritten*BytesPerSector); +++LocatorsWritten; +hVHD->Dynamic.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Dynamic, sizeof(hVHD->Dynamic))); +hVHD->Footer.Checksum = 0; +hVHD->Footer.DiskType = NtoHl (VHD_DT_Differencing); +memcpy (hVHD->Footer.DriveType, ParentFooter.DriveType, sizeof (hVHD->Footer.DriveType)); +hVHD->Footer.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Footer, sizeof(hVHD->Footer))); + +if (WriteFilePosition (hVHD->File, + &hVHD->Footer, + sizeof (hVHD->Footer), + NULL, + 0)) { + Status = errno; + goto Cleanup_Return; + } +if (WriteFilePosition (hVHD->File, + &hVHD->Dynamic, + sizeof (hVHD->Dynamic), + NULL, + NtoHll (hVHD->Footer.DataOffset))) { + Status = errno; + goto Cleanup_Return; + } +LocatorsWritten = 0; +if (RelativeMatch) { + if (WriteFilePosition (hVHD->File, + RelativeParentVHDPath, + BytesPerSector, + NULL, + LocatorPosition+LocatorsWritten*BytesPerSector)) { + Status = errno; + goto Cleanup_Return; + } + ++LocatorsWritten; + } +if (WriteFilePosition (hVHD->File, + FullParentVHDPath, + BytesPerSector, + NULL, + LocatorPosition+LocatorsWritten*BytesPerSector)) { + Status = errno; + goto Cleanup_Return; + } +++LocatorsWritten; +if (RelativeMatch) { + if (WriteFilePosition (hVHD->File, + RelativeParentVHDPathBuffer, + BytesPerSector, + NULL, + LocatorPosition+LocatorsWritten*BytesPerSector)) { + Status = errno; + goto Cleanup_Return; + } + ++LocatorsWritten; + } +if (WriteFilePosition (hVHD->File, + FullParentVHDPathBuffer, + BytesPerSector, + NULL, + LocatorPosition+LocatorsWritten*BytesPerSector)) { + Status = errno; + goto Cleanup_Return; + } +++LocatorsWritten; +if (WriteFilePosition (hVHD->File, + &hVHD->Footer, + sizeof(hVHD->Footer), + NULL, + LocatorPosition+LocatorsWritten*BytesPerSector)) { + Status = errno; + goto Cleanup_Return; + } + +Cleanup_Return: +free (RelativeParentVHDPath); +free (FullParentVHDPath); +free (RelativeParentVHDPathBuffer); +free (FullParentVHDPathBuffer); +free (FullVHDPath); +sim_vhd_disk_close ((FILE *)hVHD); +hVHD = NULL; +if (Status) { + if (EEXIST != Status) + remove (szVHDPath); + } +else { + hVHD = (VHDHANDLE)sim_vhd_disk_open (szVHDPath, "rb+"); + if (!hVHD) + Status = errno; + } +errno = Status; +return hVHD; +} + +static FILE *sim_vhd_disk_create (const char *szVHDPath, t_addr desiredsize) +{ +return (FILE *)CreateVirtualDisk (szVHDPath, (uint32)(desiredsize/512), 0, (sim_switches & SWMASK ('X'))); +} + +static FILE *sim_vhd_disk_create_diff (const char *szVHDPath, const char *szParentVHDPath) +{ +return (FILE *)CreateDifferencingVirtualDisk (szVHDPath, szParentVHDPath); +} + +static t_stat +ReadVirtualDiskSectors(VHDHANDLE hVHD, + uint8 *buf, + t_seccnt sects, + t_seccnt *sectsread, + uint32 SectorSize, + t_lba lba) +{ +uint64 BlockOffset = ((uint64)lba)*SectorSize; +uint32 BlocksRead = 0; +uint32 SectorsInRead; +size_t BytesRead; + +if (!hVHD || (hVHD->File == NULL)) { + errno = EBADF; + return SCPE_IOERR; + } +if ((BlockOffset + sects*SectorSize) > (uint64)NtoHll (hVHD->Footer.CurrentSize)) { + errno = ERANGE; + return SCPE_IOERR; + } +if (NtoHl (hVHD->Footer.DiskType) == VHD_DT_Fixed) { + if (ReadFilePosition(hVHD->File, + buf, + sects*SectorSize, + &BytesRead, + BlockOffset)) { + if (sectsread) + *sectsread = (t_seccnt)(BytesRead/SectorSize); + return SCPE_IOERR; + } + if (sectsread) + *sectsread /= SectorSize; + return SCPE_OK; + } +/* We are now dealing with a Dynamically expanding or differencing disk */ +while (sects) { + uint32 SectorsPerBlock = NtoHl (hVHD->Dynamic.BlockSize)/SectorSize; + uint64 BlockNumber = lba/SectorsPerBlock; + uint32 BitMapBytes = (7+(NtoHl (hVHD->Dynamic.BlockSize)/SectorSize))/8; + uint32 BitMapSectors = (BitMapBytes+SectorSize-1)/SectorSize; + + SectorsInRead = 1; + if (hVHD->BAT[BlockNumber] == VHD_BAT_FREE_ENTRY) { + if (!hVHD->Parent) + memset (buf, 0, SectorSize); + else { + SectorsInRead = SectorsPerBlock - lba%SectorsPerBlock; + if (SectorsInRead > sects) + SectorsInRead = sects; + if (ReadVirtualDiskSectors(hVHD->Parent, + buf, + SectorsInRead, + NULL, + SectorSize, + lba)) { + if (sectsread) + *sectsread = BlocksRead; + return FALSE; + } + } + } + else { + BlockOffset = SectorSize*((uint64)(NtoHl (hVHD->BAT[BlockNumber]) + lba%SectorsPerBlock + BitMapSectors)); + SectorsInRead = SectorsPerBlock - lba%SectorsPerBlock; + if (SectorsInRead > sects) + SectorsInRead = sects; + if (ReadFilePosition(hVHD->File, + buf, + SectorsInRead*SectorSize, + NULL, + BlockOffset)) { + if (sectsread) + *sectsread = BlocksRead; + return SCPE_IOERR; + } + } + sects -= SectorsInRead; + buf = (uint8 *)(((char *)buf) + SectorSize*SectorsInRead); + lba += SectorsInRead; + BlocksRead += SectorsInRead; + } +if (sectsread) + *sectsread = BlocksRead; +return SCPE_OK; +} + +static t_stat sim_vhd_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +VHDHANDLE hVHD = (VHDHANDLE)uptr->fileref; +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; + +return ReadVirtualDiskSectors(hVHD, buf, sects, sectsread, ctx->sector_size, lba); +} + +static t_bool +BufferIsZeros(void *Buffer, size_t BufferSize) +{ +size_t i; +char *c = (char *)Buffer; + +for (i=0; iFile) { + errno = EBADF; + return SCPE_IOERR; + } +if ((BlockOffset + sects*SectorSize) > (uint64)NtoHll(hVHD->Footer.CurrentSize)) { + errno = ERANGE; + return SCPE_IOERR; + } +if (NtoHl(hVHD->Footer.DiskType) == VHD_DT_Fixed) { + if (WriteFilePosition(hVHD->File, + buf, + sects*SectorSize, + &BytesWritten, + BlockOffset)) { + if (sectswritten) + *sectswritten = (t_seccnt)(BytesWritten/SectorSize); + return SCPE_IOERR; + } + if (sectswritten) + *sectswritten /= SectorSize; + return SCPE_OK; + } +/* We are now dealing with a Dynamically expanding or differencing disk */ +while (sects) { + uint32 SectorsPerBlock = NtoHl(hVHD->Dynamic.BlockSize)/SectorSize; + uint64 BlockNumber = lba/SectorsPerBlock; + uint32 BitMapBytes = (7+(NtoHl(hVHD->Dynamic.BlockSize)/SectorSize))/8; + uint32 BitMapSectors = (BitMapBytes+SectorSize-1)/SectorSize; + + if (BlockNumber >= NtoHl(hVHD->Dynamic.MaxTableEntries)) { + if (sectswritten) + *sectswritten = BlocksWritten; + return SCPE_EOF; + } + SectorsInWrite = 1; + if (hVHD->BAT[BlockNumber] == VHD_BAT_FREE_ENTRY) { + void *BitMap = NULL; + void *BlockData = NULL; + + if (!hVHD->Parent && BufferIsZeros(buf, SectorSize)) + goto IO_Done; + /* Need to allocate a new Data Block. */ + BlockOffset = sim_fsize_ex (hVHD->File); + if (((int64)BlockOffset) == -1) + return SCPE_IOERR; + BitMap = malloc(BitMapSectors*SectorSize); + memset(BitMap, 0xFF, BitMapBytes); + BlockOffset -= sizeof(hVHD->Footer); + hVHD->BAT[BlockNumber] = NtoHl((uint32)(BlockOffset/SectorSize)); + if (WriteFilePosition(hVHD->File, + BitMap, + BitMapSectors*SectorSize, + NULL, + BlockOffset)) { + free (BitMap); + return SCPE_IOERR; + } + free(BitMap); + BitMap = NULL; + BlockOffset += SectorSize * (SectorsPerBlock + BitMapSectors); + if (WriteFilePosition(hVHD->File, + &hVHD->Footer, + sizeof(hVHD->Footer), + NULL, + BlockOffset)) + goto Fatal_IO_Error; + if (WriteFilePosition(hVHD->File, + hVHD->BAT, + SectorSize*((sizeof(*hVHD->BAT)*NtoHl(hVHD->Dynamic.MaxTableEntries)+511)/512), + NULL, + NtoHll(hVHD->Dynamic.TableOffset))) + goto Fatal_IO_Error; + if (hVHD->Parent) + { /* Need to populate data block contents from parent VHD */ + BlockData = malloc(SectorsPerBlock*SectorSize); + if (ReadVirtualDiskSectors(hVHD->Parent, + BlockData, + SectorsPerBlock, + NULL, + SectorSize, + (lba/SectorsPerBlock)*SectorsPerBlock)) + goto Fatal_IO_Error; + if (WriteVirtualDiskSectors(hVHD, + BlockData, + SectorsPerBlock, + NULL, + SectorSize, + (lba/SectorsPerBlock)*SectorsPerBlock)) + goto Fatal_IO_Error; + free(BlockData); + } + continue; +Fatal_IO_Error: + free (BitMap); + free (BlockData); + fclose (hVHD->File); + hVHD->File = NULL; + return SCPE_IOERR; + } + else { + BlockOffset = 512*((uint64)(NtoHl(hVHD->BAT[BlockNumber]) + lba%SectorsPerBlock + BitMapSectors)); + SectorsInWrite = SectorsPerBlock - lba%SectorsPerBlock; + if (SectorsInWrite > sects) + SectorsInWrite = sects; + if (WriteFilePosition(hVHD->File, + buf, + SectorsInWrite*SectorSize, + NULL, + BlockOffset)) { + if (sectswritten) + *sectswritten = BlocksWritten; + return SCPE_IOERR; + } + } +IO_Done: + sects -= SectorsInWrite; + buf = (uint8 *)(((char *)buf) + SectorsInWrite*SectorSize); + lba += SectorsInWrite; + BlocksWritten += SectorsInWrite; + } +if (sectswritten) + *sectswritten = BlocksWritten; +return SCPE_OK; +} + +static t_stat sim_vhd_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +VHDHANDLE hVHD = (VHDHANDLE)uptr->fileref; +struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; + +return WriteVirtualDiskSectors(hVHD, buf, sects, sectswritten, ctx->sector_size, lba); +} +#endif diff --git a/sim_disk.h b/sim_disk.h new file mode 100644 index 00000000..3fa70e9d --- /dev/null +++ b/sim_disk.h @@ -0,0 +1,86 @@ +/* sim_disk.h: simulator disk support library definitions + + Copyright (c) 2011, Mark Pizzolato + + 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 names of Robert M Supnik and + Mark Pizzolato 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 and Mark Pizzolato. + + 25-Jan-11 MP Initial Implemementation +*/ + +#ifndef _SIM_DISK_H_ +#define _SIM_DISK_H_ 0 + +/* SIMH/Disk format */ + +typedef uint32 t_seccnt; /* disk sector count */ +typedef uint32 t_lba; /* disk logical block address */ + +/* Unit flags */ + +#define DKUF_V_WLK (UNIT_V_UF + 12) /* write locked */ +#define DKUF_V_FMT (UNIT_V_UF + 13) /* disk file format */ +#define DKUF_W_FMT 3 /* 3b of formats */ +#define DKUF_N_FMT (1u << DKUF_W_FMT) /* number of formats */ +#define DKUF_M_FMT ((1u << DKUF_W_FMT) - 1) +#define DKUF_F_STD 0 /* SIMH format */ +#define DKUF_F_RAW 1 /* Raw Physical Disk Access */ +#define DKUF_F_VHD 2 /* VHD format */ +#define DKUF_V_UF (DKUF_V_FMT + DKUF_W_FMT) +#define DKUF_WLK (1u << DKUF_V_WLK) +#define DKUF_FMT (DKUF_M_FMT << DKUF_V_FMT) +#define DKUF_WRP (DKUF_WLK | UNIT_RO) + +#define DK_F_STD (DKUF_F_STD << DKUF_V_FMT) +#define DK_F_RAW (DKUF_F_RAW << DKUF_V_FMT) +#define DK_F_VHD (DKUF_F_VHD << DKUF_V_FMT) + +#define DK_GET_FMT(u) (((u)->flags >> DKUF_V_FMT) & DKUF_M_FMT) + +/* Return status codes */ + +#define DKSE_OK 0 /* no error */ + +typedef void (*DISK_PCALLBACK)(UNIT *unit, t_stat status); + +/* Prototypes */ + +t_stat sim_disk_attach (UNIT *uptr, char *cptr, size_t sector_size, size_t xfer_element_size, t_bool dontautosize, uint32 debugbit, const char *drivetype, uint32 pdp11_tracksize); +t_stat sim_disk_detach (UNIT *uptr); +t_stat sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects); +t_stat sim_disk_rdsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects, DISK_PCALLBACK callback); +t_stat sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects); +t_stat sim_disk_wrsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects, DISK_PCALLBACK callback); +t_stat sim_disk_unload (UNIT *uptr); +t_stat sim_disk_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sim_disk_show_fmt (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat sim_disk_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sim_disk_show_capac (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat sim_disk_set_asynch (UNIT *uptr, int latency); +t_stat sim_disk_clr_asynch (UNIT *uptr); +t_bool sim_disk_isavailable (UNIT *uptr); +t_bool sim_disk_isavailable_a (UNIT *uptr, DISK_PCALLBACK callback); +t_bool sim_disk_wrp (UNIT *uptr); +t_addr sim_disk_size (UNIT *uptr); +void sim_disk_data_trace (UNIT *uptr, const uint8 *data, size_t lba, size_t len, const char* txt, int detail, uint32 reason); + +#endif diff --git a/sim_fio.c b/sim_fio.c index 70b2800a..b9a91ecb 100644 --- a/sim_fio.c +++ b/sim_fio.c @@ -23,6 +23,8 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 02-Feb-11 MP Added sim_fsize_ex and sim_fsize_name_ex returning t_addr + Added export of sim_buf_copy_swapped and sim_buf_swap_data 28-Jun-07 RMS Added VMS IA64 support (from Norm Lastovica) 10-Jul-06 RMS Fixed linux conditionalization (from Chaskiel Grundman) 15-May-06 RMS Added sim_fsize_name @@ -35,12 +37,17 @@ This library includes: - sim_finit - initialize package - sim_fopen - open file - sim_fread - endian independent read (formerly fxread) - sim_write - endian independent write (formerly fxwrite) - sim_fseek - extended (>32b) seek (formerly fseek_ext) - sim_fsize - get file size + sim_finit - initialize package + sim_fopen - open file + sim_fread - endian independent read (formerly fxread) + sim_write - endian independent write (formerly fxwrite) + sim_fseek - extended (>32b) seek (formerly fseek_ext) + sim_fsize - get file size + sim_fsize_name - get file size of named file + sim_fsize_ex - get file size as a t_addr + sim_fsize_name_ex - get file size as a t_addr of named file + sim_buf_copy_swapped - copy data swapping elements along the way + sim_buf_swap_data - swap data elements inplace in buffer sim_fopen and sim_fseek are OS-dependent. The other routines are not. sim_fsize is always a 32b routine (it is used only with small capacity random @@ -78,18 +85,16 @@ sim_end = end_test.c[0]; return sim_end; } -size_t sim_fread (void *bptr, size_t size, size_t count, FILE *fptr) +void sim_buf_swap_data (void *bptr, size_t size, size_t count) { -size_t c, j; +uint32 j; int32 k; unsigned char by, *sptr, *dptr; -if ((size == 0) || (count == 0)) /* check arguments */ - return 0; -c = fread (bptr, size, count, fptr); /* read buffer */ -if (sim_end || (size == sizeof (char)) || (c == 0)) /* le, byte, or err? */ - return c; /* done */ -for (j = 0, dptr = sptr = (unsigned char *) bptr; j < c; j++) { /* loop on items */ +if (sim_end || (count == 0) || (size == sizeof (char))) + return; +for (j = 0, dptr = sptr = (unsigned char *) bptr; /* loop on items */ + j < count; j++) { for (k = size - 1; k >= (((int32) size + 1) / 2); k--) { by = *sptr; /* swap end-for-end */ *sptr++ = *(dptr + k); @@ -97,14 +102,44 @@ for (j = 0, dptr = sptr = (unsigned char *) bptr; j < c; j++) { /* loop on items } sptr = dptr = dptr + size; /* next item */ } +} + +size_t sim_fread (void *bptr, size_t size, size_t count, FILE *fptr) +{ +size_t c; + +if ((size == 0) || (count == 0)) /* check arguments */ + return 0; +c = fread (bptr, size, count, fptr); /* read buffer */ +if (sim_end || (size == sizeof (char)) || (c == 0)) /* le, byte, or err? */ + return c; /* done */ +sim_buf_swap_data (bptr, size, count); return c; } +void sim_buf_copy_swapped (void *dbuf, void *sbuf, size_t size, size_t count) +{ +size_t j; +int32 k; +unsigned char *sptr = (unsigned char *)sbuf; +unsigned char *dptr = (unsigned char *)dbuf; + +if (sim_end || (size == sizeof (char))) { + memcpy (dptr, sptr, size * count); + return; + } +for (j = 0; j < count; j++) { /* loop on items */ + for (k = size - 1; k >= 0; k--) + *(dptr + k) = *sptr++; + dptr = dptr + size; + } +} + size_t sim_fwrite (void *bptr, size_t size, size_t count, FILE *fptr) { -size_t c, j, nelem, nbuf, lcnt, total; -int32 i, k; -unsigned char *sptr, *dptr; +size_t c, nelem, nbuf, lcnt, total; +int32 i; +unsigned char *sptr; if ((size == 0) || (count == 0)) /* check arguments */ return 0; @@ -119,11 +154,8 @@ total = 0; sptr = (unsigned char *) bptr; /* init input ptr */ for (i = nbuf; i > 0; i--) { /* loop on buffers */ c = (i == 1)? lcnt: nelem; - for (j = 0, dptr = sim_flip; j < c; j++) { /* loop on items */ - for (k = size - 1; k >= 0; k--) - *(dptr + k) = *sptr++; - dptr = dptr + size; - } + sim_buf_copy_swapped (sim_flip, sptr, size, c); + sptr = sptr + size * count; c = fwrite (sim_flip, size, c, fptr); if (c == 0) return total; @@ -132,31 +164,45 @@ for (i = nbuf; i > 0; i--) { /* loop on buffers */ return total; } +/* Forward Declaration */ + +static t_addr _sim_ftell (FILE *st); + /* Get file size */ -uint32 sim_fsize_name (char *fname) +t_addr sim_fsize_ex (FILE *fp) +{ +t_addr pos, sz; + +if (fp == NULL) + return 0; +pos = _sim_ftell (fp); +sim_fseek (fp, 0, SEEK_END); +sz = _sim_ftell (fp); +sim_fseek (fp, pos, SEEK_SET); +return sz; +} + +t_addr sim_fsize_name_ex (char *fname) { FILE *fp; -uint32 sz; +t_addr sz; if ((fp = sim_fopen (fname, "rb")) == NULL) return 0; -sz = sim_fsize (fp); +sz = sim_fsize_ex (fp); fclose (fp); return sz; } +uint32 sim_fsize_name (char *fname) +{ +return (uint32)(sim_fsize_name_ex (fname)); +} + uint32 sim_fsize (FILE *fp) { -uint32 pos, sz; - -if (fp == NULL) - return 0; -pos = ftell (fp); -fseek (fp, 0, SEEK_END); -sz = ftell (fp); -fseek (fp, pos, SEEK_SET); -return sz; +return (uint32)(sim_fsize_ex (fp)); } /* OS-dependent routines */ @@ -227,6 +273,9 @@ switch (whence) { fileaddr = offset; break; + case SEEK_END: + if (_fseeki64 (st, 0, SEEK_END)) + return (-1); case SEEK_CUR: if (fgetpos (st, &filepos)) return (-1); @@ -243,6 +292,14 @@ int64_to_fpos_t (fileaddr, &filepos, 127); return fsetpos (st, &filepos); } +static t_addr _sim_ftell (FILE *st) +{ +fpos_t fileaddr; +if (fgetpos (st, &fileaddr)) + return (-1); +return (t_addr)fpos_t_to_int64 (&fileaddr); +} + #endif /* Alpha UNIX - natively 64b */ @@ -255,16 +312,23 @@ int sim_fseek (FILE *st, t_addr offset, int whence) return fseek (st, offset, whence); } +static t_addr _sim_ftell (FILE *st) +{ +return (t_addr)(ftell (st)); +} + #endif /* Windows */ #if defined (_WIN32) #define _SIM_IO_FSEEK_EXT_ 1 +#include int sim_fseek (FILE *st, t_addr offset, int whence) { fpos_t fileaddr; +struct _stati64 statb; switch (whence) { @@ -272,6 +336,11 @@ switch (whence) { fileaddr = offset; break; + case SEEK_END: + if (_fstati64 (_fileno (st), &statb)) + return (-1); + fileaddr = statb.st_size + offset; + break; case SEEK_CUR: if (fgetpos (st, &fileaddr)) return (-1); @@ -286,6 +355,14 @@ switch (whence) { return fsetpos (st, &fileaddr); } +static t_addr _sim_ftell (FILE *st) +{ +fpos_t fileaddr; +if (fgetpos (st, &fileaddr)) + return (-1); +return (t_addr)fileaddr; +} + #endif /* end Windows */ /* Linux */ @@ -298,6 +375,11 @@ int sim_fseek (FILE *st, t_addr xpos, int origin) return fseeko64 (st, xpos, origin); } +static t_addr _sim_ftell (FILE *st) +{ +return (t_addr)(ftello64 (st)); +} + #endif /* end Linux with LFS */ /* Apple OS/X */ @@ -310,6 +392,11 @@ int sim_fseek (FILE *st, t_addr xpos, int origin) return fseeko (st, xpos, origin); } +static t_addr _sim_ftell (FILE *st) +{ +return (t_addr)(ftello (st)); +} + #endif /* end Apple OS/X */ #endif /* end 64b seek defs */ @@ -324,6 +411,11 @@ int sim_fseek (FILE *st, t_addr xpos, int origin) return fseek (st, (int32) xpos, origin); } +static t_addr _sim_ftell (FILE *st) +{ +return (t_addr)(ftell (st)); +} + #endif uint32 sim_taddr_64 = _SIM_IO_FSEEK_EXT_; diff --git a/sim_fio.h b/sim_fio.h index 264ea971..a6985426 100644 --- a/sim_fio.h +++ b/sim_fio.h @@ -23,6 +23,8 @@ 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. + 02-Feb-11 MP Added sim_fsize_ex and sim_fsize_name_ex returning t_addr + Added export of sim_buf_copy_swapped and sim_buf_swap_data 15-May-06 RMS Added sim_fsize_name 16-Aug-05 RMS Fixed C++ declaration and cast problems 02-Jan-04 RMS Split out from SCP @@ -42,5 +44,10 @@ size_t sim_fread (void *bptr, size_t size, size_t count, FILE *fptr); size_t sim_fwrite (void *bptr, size_t size, size_t count, FILE *fptr); uint32 sim_fsize (FILE *fptr); uint32 sim_fsize_name (char *fname); +t_addr sim_fsize_ex (FILE *fptr); +t_addr sim_fsize_name_ex (char *fname); +void sim_buf_swap_data (void *bptr, size_t size, size_t count); +void sim_buf_copy_swapped (void *dptr, void *bptr, size_t size, size_t count); + #endif diff --git a/sim_tape.c b/sim_tape.c index c3a0c9b2..d50a4887 100644 --- a/sim_tape.c +++ b/sim_tape.c @@ -26,6 +26,17 @@ Ultimately, this will be a place to hide processing of various tape formats, as well as OS-specific direct hardware access. + 05-Feb-11 MP Refactored to prepare for SIM_ASYNC_IO support + Added higher level routines: + sim_tape_wreomrw - erase remainder of tape & rewind + sim_tape_sprecsf - skip records + sim_tape_spfilef - skip files + sim_tape_sprecsr - skip records rev + sim_tape_spfiler - skip files rev + sim_tape_position - general purpose position + These routines correspond to natural tape operations + and will align better when physical tape support is + included here. 08-Jun-08 JDB Fixed signed/unsigned warning in sim_tape_set_fmt 23-Jan-07 JDB Fixed backspace over gap at BOT 22-Jan-07 RMS Fixed bug in P7B format read reclnt rev (found by Rich Cornwell) @@ -53,7 +64,13 @@ sim_tape_sprecr space tape record reverse sim_tape_wrtmk write tape mark sim_tape_wreom erase remainder of tape + sim_tape_wreomrw erase remainder of tape & rewind sim_tape_wrgap write erase gap + sim_tape_sprecsf space records forward + sim_tape_spfilef space files forward + sim_tape_sprecsr space records reverse + sim_tape_spfiler space files reverse + sim_tape_position generalized position sim_tape_rewind rewind sim_tape_reset reset unit sim_tape_bot TRUE if at beginning of tape @@ -63,10 +80,17 @@ sim_tape_show_fmt show tape format sim_tape_set_capac set tape capacity sim_tape_show_capac show tape capacity + sim_tape_set_async enable asynchronous operation + sim_tape_clr_async disable asynchronous operation */ #include "sim_defs.h" #include "sim_tape.h" +#include + +#if defined SIM_ASYNCH_IO +#include +#endif struct sim_tape_fmt { char *name; /* name */ @@ -90,14 +114,276 @@ t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat); uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map); t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map); +struct tape_context { + DEVICE *dptr; /* Device for unit (access to debug flags) */ + uint32 dbit; /* debugging bit */ +#if defined SIM_ASYNCH_IO + int asynch_io; /* Asynchronous Interrupt scheduling enabled */ + int asynch_io_latency; /* instructions to delay pending interrupt */ + pthread_mutex_t lock; + pthread_t io_thread; /* I/O Thread Id */ + pthread_mutex_t io_lock; + pthread_cond_t io_cond; + int io_top; + uint8 *buf; + uint32 *bc; + uint32 *fc; + uint32 vbc; + uint32 max; + uint32 gaplen; + uint32 bpi; + uint32 *objupdate; + TAPE_PCALLBACK callback; + t_stat io_status; +#endif + }; +#define tape_ctx up8 /* Field in Unit structure which points to the tape_context */ + +#if defined SIM_ASYNCH_IO +#define AIO_CALLSETUP \ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; \ + \ +if ((!callback) || !ctx->asynch_io) + +#define AIO_CALL(op, _buf, _bc, _fc, _max, _vbc, _gaplen, _bpi, _obj, _callback)\ + if (1) { \ + struct tape_context *ctx = \ + (struct tape_context *)uptr->tape_ctx; \ + \ + \ + sim_debug (ctx->dbit, ctx->dptr, \ + "sim_tape AIO_CALL(op=%d, unit=%d)\n", op, uptr-ctx->dptr->units);\ + \ + if (ctx->callback) \ + abort(); /* horrible mistake, stop */ \ + ctx->io_top = op; \ + ctx->buf = _buf; \ + ctx->bc = _bc; \ + ctx->fc = _fc; \ + ctx->max = _max; \ + ctx->vbc = _vbc; \ + ctx->gaplen = _gaplen; \ + ctx->bpi = _bpi; \ + ctx->objupdate = _obj; \ + ctx->callback = _callback; \ + pthread_cond_signal (&ctx->io_cond); \ + } +#define TOP_DONE 0 /* close */ +#define TOP_RDRF 1 /* sim_tape_rdrecf_a */ +#define TOP_RDRR 2 /* sim_tape_rdrecr_a */ +#define TOP_WREC 3 /* sim_tape_wrrecf_a */ +#define TOP_WTMK 4 /* sim_tape_wrtmk_a */ +#define TOP_WEOM 5 /* sim_tape_wreom_a */ +#define TOP_WEMR 6 /* sim_tape_wreomrw_a */ +#define TOP_WGAP 7 /* sim_tape_wrgap_a */ +#define TOP_SPRF 8 /* sim_tape_sprecf_a */ +#define TOP_SRSF 9 /* sim_tape_sprecsf_a */ +#define TOP_SPRR 10 /* sim_tape_sprecr_a */ +#define TOP_SRSR 11 /* sim_tape_sprecsr_a */ +#define TOP_SPFF 12 /* sim_tape_spfilef */ +#define TOP_SFRF 13 /* sim_tape_spfilebyrecf */ +#define TOP_SPFR 14 /* sim_tape_spfiler */ +#define TOP_SFRR 15 /* sim_tape_spfilebyrecr */ +#define TOP_RWND 16 /* sim_tape_rewind_a */ +#define TOP_POSN 17 /* sim_tape_position_a */ + +static void * +_tape_io(void *arg) +{ +UNIT* volatile uptr = (UNIT*)arg; +int sched_policy; +struct sched_param sched_priority; +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; + + /* Boost Priority for this I/O thread vs the CPU instruction execution + thread which in general won't be readily yielding the processor when + this thread needs to run */ + pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); + ++sched_priority.sched_priority; + pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); + + sim_debug (ctx->dbit, ctx->dptr, "_tape_io(unit=%d) starting\n", uptr-ctx->dptr->units); + + pthread_mutex_lock (&ctx->io_lock); + while (1) { + pthread_cond_wait (&ctx->io_cond, &ctx->io_lock); + if (ctx->io_top == TOP_DONE) + break; + pthread_mutex_unlock (&ctx->io_lock); + switch (ctx->io_top) { + case TOP_RDRF: + ctx->io_status = sim_tape_rdrecf (uptr, ctx->buf, ctx->bc, ctx->max); + break; + case TOP_RDRR: + ctx->io_status = sim_tape_rdrecr (uptr, ctx->buf, ctx->bc, ctx->max); + break; + case TOP_WREC: + ctx->io_status = sim_tape_wrrecf (uptr, ctx->buf, ctx->vbc); + break; + case TOP_WTMK: + ctx->io_status = sim_tape_wrtmk (uptr); + break; + case TOP_WEOM: + ctx->io_status = sim_tape_wreom (uptr); + break; + case TOP_WEMR: + ctx->io_status = sim_tape_wreomrw (uptr); + break; + case TOP_WGAP: + ctx->io_status = sim_tape_wrgap (uptr, ctx->gaplen, ctx->bpi); + break; + case TOP_SPRF: + ctx->io_status = sim_tape_sprecf (uptr, ctx->bc); + break; + case TOP_SRSF: + ctx->io_status = sim_tape_sprecsf (uptr, ctx->vbc, ctx->bc); + break; + case TOP_SPRR: + ctx->io_status = sim_tape_sprecr (uptr, ctx->bc); + break; + case TOP_SRSR: + ctx->io_status = sim_tape_sprecsr (uptr, ctx->vbc, ctx->bc); + break; + case TOP_SPFF: + ctx->io_status = sim_tape_spfilef (uptr, ctx->vbc, ctx->bc); + break; + case TOP_SFRF: + ctx->io_status = sim_tape_spfilebyrecf (uptr, ctx->vbc, ctx->bc, ctx->fc); + break; + case TOP_SPFR: + ctx->io_status = sim_tape_spfiler (uptr, ctx->vbc, ctx->bc); + break; + case TOP_SFRR: + ctx->io_status = sim_tape_spfilebyrecr (uptr, ctx->vbc, ctx->bc, ctx->fc); + break; + case TOP_RWND: + ctx->io_status = sim_tape_rewind (uptr); + break; + case TOP_POSN: + ctx->io_status = sim_tape_position (uptr, ctx->vbc, ctx->gaplen, ctx->bc, ctx->bpi, ctx->fc, ctx->objupdate); + break; + } + pthread_mutex_lock (&ctx->io_lock); + ctx->io_top = TOP_DONE; + sim_activate (uptr, ctx->asynch_io_latency); + } + pthread_mutex_unlock (&ctx->io_lock); + + sim_debug (ctx->dbit, ctx->dptr, "_tape_io(unit=%d) exiting\n", uptr-ctx->dptr->units); + + return NULL; +} + +/* This routine is called in the context of the main simulator thread before + processing events for any unit. It is only called when an asynchronous + thread has called sim_activate() to activate a unit. The job of this + routine is to put the unit in proper condition to digest what may have + occurred in the asynchrcondition thread. + + Since tape processing only handles a single I/O at a time to a + particular tape device, we have the opportunity to possibly detect + improper attempts to issue multiple concurrent I/O requests. */ +static void _tape_completion_dispatch (UNIT *uptr) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +TAPE_PCALLBACK callback = ctx->callback; + +sim_debug (ctx->dbit, ctx->dptr, "_tape_completion_dispatch(unit=%d, top=%d, callback=%p)\n", uptr-ctx->dptr->units, ctx->io_top, ctx->callback); + +if (ctx->io_top != TOP_DONE) + abort(); /* horribly wrong, stop */ + +if (ctx->callback && ctx->io_top == TOP_DONE) { + ctx->callback = NULL; + callback (uptr, ctx->io_status); + } +} +#else +#define AIO_CALLSETUP +#define AIO_CALL(op, _buf, _fc, _bc, _max, _vbc, _gaplen, _bpi, _obj, _callback) \ + if (_callback) \ + (_callback) (uptr, r); +#endif + + +/* Enable asynchronous operation */ + +t_stat sim_tape_set_async (UNIT *uptr, int latency) +{ +#if !defined(SIM_ASYNCH_IO) +extern FILE *sim_log; /* log file */ +char *msg = "Tape: can't operate asynchronously\r\n"; +printf ("%s", msg); +if (sim_log) fprintf (sim_log, "%s", msg); +return SCPE_NOFNC; +#else +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +pthread_attr_t attr; + +ctx->asynch_io = 1; +ctx->asynch_io_latency = latency; +pthread_mutex_init (&ctx->io_lock, NULL); +pthread_cond_init (&ctx->io_cond, NULL); +pthread_attr_init(&attr); +pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); +pthread_create (&ctx->io_thread, &attr, _tape_io, (void *)uptr); +pthread_attr_destroy(&attr); +uptr->a_check_completion = _tape_completion_dispatch; +#endif +return SCPE_OK; +} + +/* Disable asynchronous operation */ + +t_stat sim_tape_clr_async (UNIT *uptr) +{ +#if !defined(SIM_ASYNCH_IO) +return SCPE_NOFNC; +#else +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; + +/* make sure device exists */ +if (!ctx) return SCPE_UNATT; + +if (ctx->asynch_io) { + pthread_mutex_lock (&ctx->io_lock); + ctx->asynch_io = 0; + pthread_cond_signal (&ctx->io_cond); + pthread_mutex_unlock (&ctx->io_lock); + pthread_join (ctx->io_thread, NULL); + pthread_mutex_destroy (&ctx->io_lock); + pthread_cond_destroy (&ctx->io_cond); + } +return SCPE_OK; +#endif +} + +static void _sim_tape_io_flush (UNIT *uptr) +{ +#if defined (SIM_ASYNCH_IO) +sim_tape_clr_async (uptr); +sim_tape_set_async (uptr, 0); +#endif +fflush (uptr->fileref); +} + /* Attach tape unit */ t_stat sim_tape_attach (UNIT *uptr, char *cptr) { +return sim_tape_attach_ex (uptr, cptr, 0); +} + +t_stat sim_tape_attach_ex (UNIT *uptr, char *cptr, uint32 dbit) +{ +struct tape_context *ctx; uint32 objc; +DEVICE *dptr; char gbuf[CBUFSIZE]; t_stat r; +if ((dptr = find_dev_from_unit (uptr)) == NULL) + return SCPE_NOATT; if (sim_switches & SWMASK ('F')) { /* format spec? */ cptr = get_glyph (cptr, gbuf, 0); /* get spec */ if (*cptr == 0) /* must be more */ @@ -129,7 +415,17 @@ switch (MT_GET_FMT (uptr)) { /* case on format */ break; } +uptr->tape_ctx = ctx = (struct tape_context *)calloc(1, sizeof(struct tape_context)); +ctx->dptr = dptr; /* save DEVICE pointer */ +ctx->dbit = dbit; /* save debug bit */ + sim_tape_rewind (uptr); + +#if defined (SIM_ASYNCH_IO) +sim_tape_set_async (uptr, 0); +#endif +uptr->io_flush = _sim_tape_io_flush; + return SCPE_OK; } @@ -140,6 +436,10 @@ t_stat sim_tape_detach (UNIT *uptr) uint32 f = MT_GET_FMT (uptr); t_stat r; +#if defined (SIM_ASYNCH_IO) +sim_tape_clr_async (uptr); +#endif + r = detach_unit (uptr); /* detach unit */ if (r != SCPE_OK) return r; @@ -157,9 +457,52 @@ switch (f) { /* case on format */ } sim_tape_rewind (uptr); +free (uptr->tape_ctx); +uptr->tape_ctx = NULL; +uptr->io_flush = NULL; return SCPE_OK; } +void sim_tape_data_trace(UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; + +if (ctx->dptr->dctrl & reason) { + sim_debug (reason, ctx->dptr, "%s%d %s len: %08X\n", ctx->dptr->name, uptr-ctx->dptr->units, txt, len); + if (detail) { + size_t i, same, group, sidx, oidx; + char outbuf[80], strbuf[18]; + static char hex[] = "0123456789ABCDEF"; + + for (i=same=0; i 0) && (0 == memcmp (&data[i], &data[i-16], 16))) { + ++same; + continue; + } + if (same > 0) { + sim_debug (reason, ctx->dptr, "%04X thru %04X same as above\n", i-(16*same), i-1); + same = 0; + } + group = (((len - i) > 16) ? 16 : (len - i)); + for (sidx=oidx=0; sidx>4)&0xf]; + outbuf[oidx++] = hex[data[i+sidx]&0xf]; + if (isprint (data[i+sidx])) + strbuf[sidx] = data[i+sidx]; + else + strbuf[sidx] = '.'; + } + outbuf[oidx] = '\0'; + strbuf[sidx] = '\0'; + sim_debug (reason, ctx->dptr, "%04X%-48s %s\n", i, outbuf, strbuf); + } + if (same > 0) + sim_debug (reason, ctx->dptr, "%04X thru %04X same as above\n", i-(16*same), len-1); + } + } +} + /* Read record length forward (internal routine) Inputs: @@ -398,11 +741,14 @@ return MTSE_OK; t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max) { +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; uint32 f = MT_GET_FMT (uptr); t_mtrlnt i, tbc, rbc; t_addr opos; t_stat st; +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecf(unit=%d, buf=%p, max=%d)\n", uptr-ctx->dptr->units, buf, max); + opos = uptr->pos; /* old position */ if (st = sim_tape_rdlntf (uptr, &tbc)) /* read rec lnt */ return st; @@ -425,6 +771,16 @@ if (f == MTUF_F_P7B) /* p7b? strip SOR */ return (MTR_F (tbc)? MTSE_RECE: MTSE_OK); } +t_stat sim_tape_rdrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback) +{ +t_stat r = SCPE_OK; +AIO_CALLSETUP + r = sim_tape_rdrecf (uptr, buf, bc, max); +AIO_CALL(TOP_RDRF, buf, bc, NULL, max, 0, 0, 0, NULL, callback); +return r; +} + + /* Read record reverse Inputs: @@ -449,10 +805,13 @@ return (MTR_F (tbc)? MTSE_RECE: MTSE_OK); t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max) { +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; uint32 f = MT_GET_FMT (uptr); t_mtrlnt i, rbc, tbc; t_stat st; +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecr(unit=%d, buf=%p, max=%d)\n", uptr-ctx->dptr->units, buf, max); + if (st = sim_tape_rdlntr (uptr, &tbc)) /* read rec lnt */ return st; *bc = rbc = MTR_L (tbc); /* strip error flag */ @@ -468,6 +827,15 @@ if (f == MTUF_F_P7B) /* p7b? strip SOR */ return (MTR_F (tbc)? MTSE_RECE: MTSE_OK); } +t_stat sim_tape_rdrecr_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback) +{ +t_stat r = SCPE_OK; +AIO_CALLSETUP + r = sim_tape_rdrecr (uptr, buf, bc, max); +AIO_CALL(TOP_RDRR, buf, bc, NULL, max, 0, 0, 0, NULL, callback); +return r; +} + /* Write record forward Inputs: @@ -487,9 +855,12 @@ return (MTR_F (tbc)? MTSE_RECE: MTSE_OK); t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc) { +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; uint32 f = MT_GET_FMT (uptr); t_mtrlnt sbc; +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrrecf(unit=%d, buf=%p, bc=%d)\n", uptr-ctx->dptr->units, buf, bc); + MT_CLR_PNU (uptr); sbc = MTR_L (bc); if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ @@ -529,6 +900,15 @@ switch (f) { /* case on format */ return MTSE_OK; } +t_stat sim_tape_wrrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt bc, TAPE_PCALLBACK callback) +{ +t_stat r = SCPE_OK; +AIO_CALLSETUP + r = sim_tape_wrrecf (uptr, buf, bc); +AIO_CALL(TOP_WREC, buf, 0, NULL, 0, bc, 0, 0, NULL, callback); +return r; +} + /* Write metadata forward (internal routine) */ t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat) @@ -552,6 +932,9 @@ return MTSE_OK; t_stat sim_tape_wrtmk (UNIT *uptr) { +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrtmk(unit=%d)\n", uptr-ctx->dptr->units); if (MT_GET_FMT (uptr) == MTUF_F_P7B) { /* P7B? */ uint8 buf = P7B_EOF; /* eof mark */ return sim_tape_wrrecf (uptr, &buf, 1); /* write char */ @@ -559,15 +942,61 @@ if (MT_GET_FMT (uptr) == MTUF_F_P7B) { /* P7B? */ return sim_tape_wrdata (uptr, MTR_TMK); } +t_stat sim_tape_wrtmk_a (UNIT *uptr, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_wrtmk (uptr); +AIO_CALL(TOP_WTMK, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); +return r; +} + /* Write end of medium */ t_stat sim_tape_wreom (UNIT *uptr) { +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreom(unit=%d)\n", uptr-ctx->dptr->units); if (MT_GET_FMT (uptr) == MTUF_F_P7B) /* cant do P7B */ return MTSE_FMT; return sim_tape_wrdata (uptr, MTR_EOM); } +t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_wreom (uptr); +AIO_CALL(TOP_WEOM, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); +return r; +} + +/* Write end of medium-rewind */ + +t_stat sim_tape_wreomrw (UNIT *uptr) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +t_stat r; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreomrw(unit=%d)\n", uptr-ctx->dptr->units); +if (MT_GET_FMT (uptr) == MTUF_F_P7B) /* cant do P7B */ + return MTSE_FMT; +r = sim_tape_wrdata (uptr, MTR_EOM); +if (r == MTSE_OK) + r = sim_tape_rewind (uptr); +return r; +} + +t_stat sim_tape_wreomrw_a (UNIT *uptr, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_wreomrw (uptr); +AIO_CALL(TOP_WEMR, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); +return r; +} + /* Write erase gap Inputs: @@ -659,6 +1088,7 @@ return sim_tape_wrdata (uptr, MTR_EOM); t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen, uint32 bpi) { +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat st; t_mtrlnt meta, sbc, new_len, rec_size; t_addr gap_pos = uptr->pos; @@ -669,6 +1099,8 @@ int32 gap_needed = (gaplen * bpi) / 10; /* gap remainder still n const uint32 meta_size = sizeof (t_mtrlnt); /* bytes per metadatum */ const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* smallest data record size */ +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%p, bpi=%d)\n", uptr-ctx->dptr->units, gaplen, bpi); + MT_CLR_PNU (uptr); if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ @@ -794,23 +1226,126 @@ while (--marker_count > 0); return MTSE_OK; } -/* Space record forward */ +t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, uint32 bpi, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_wrgap (uptr, gaplen, bpi); +AIO_CALL(TOP_RDRR, NULL, NULL, NULL, 0, 0, gaplen, bpi, NULL, callback); +return r; +} + +/* Space record forward + + Inputs: + uptr = pointer to tape unit + bc = pointer to size of record skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged, PNU set + end of file/medium unchanged, PNU set + tape mark updated + data record updated + data record error updated +*/ t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc) { +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat st; +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecf(unit=%d)\n", uptr-ctx->dptr->units); + st = sim_tape_rdlntf (uptr, bc); /* get record length */ *bc = MTR_L (*bc); return st; } -/* Space record reverse */ +t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_sprecf (uptr, bc); +AIO_CALL(TOP_SPRF, NULL, bc, NULL, 0, 0, 0, 0, NULL, callback); +return r; +} + +/* Space records forward + + Inputs: + uptr = pointer to tape unit + count = count of records to skip + skipped = pointer to number of records actually skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged, PNU set + end of file/medium unchanged, PNU set + tape mark updated + data record updated + data record error updated +*/ + +t_stat sim_tape_sprecsf (UNIT *uptr, uint32 count, uint32 *skipped) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +t_stat st; +t_mtrlnt tbc; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecsf(unit=%d, count=%d)\n", uptr-ctx->dptr->units, count); + +*skipped = 0; +while (*skipped < count) { /* loopo */ + st = sim_tape_sprecf (uptr, &tbc); /* spc rec */ + if (st != MTSE_OK) + return st; + *skipped = *skipped + 1; /* # recs skipped */ + } +return MTSE_OK; +} + +t_stat sim_tape_sprecsf_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_sprecsf (uptr, count, skipped); +AIO_CALL(TOP_SRSF, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback); +return r; +} + +/* Space record reverse + + Inputs: + uptr = pointer to tape unit + bc = pointer to size of records skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + beginning of tape unchanged + read error unchanged + end of file unchanged + end of medium updated + tape mark updated + data record updated +*/ t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc) { +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat st; +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecr(unit=%d)\n", uptr-ctx->dptr->units); + if (MT_TST_PNU (uptr)) { MT_CLR_PNU (uptr); *bc = 0; @@ -821,15 +1356,331 @@ st = sim_tape_rdlntr (uptr, bc); /* get record length */ return st; } +t_stat sim_tape_sprecr_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_sprecr (uptr, bc); +AIO_CALL(TOP_SPRR, NULL, bc, NULL, 0, 0, 0, 0, NULL, callback); +return r; +} + +/* Space records reverse + + Inputs: + uptr = pointer to tape unit + count = count of records to skip + skipped = pointer to number of records actually skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + beginning of tape unchanged + read error unchanged + end of file unchanged + end of medium updated + tape mark updated + data record updated +*/ + +t_stat sim_tape_sprecsr (UNIT *uptr, uint32 count, uint32 *skipped) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +t_stat st; +t_mtrlnt tbc; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecsr(unit=%d, count=%d)\n", uptr-ctx->dptr->units, count); + +*skipped = 0; +while (*skipped < count) { /* loopo */ + st = sim_tape_sprecr (uptr, &tbc); /* spc rec rev */ + if (st != MTSE_OK) + return st; + *skipped = *skipped + 1; /* # recs skipped */ + } +return MTSE_OK; +} + +t_stat sim_tape_sprecsr_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_sprecsr (uptr, count, skipped); +AIO_CALL(TOP_SRSR, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback); +return r; +} + +/* Space files forward by record + + Inputs: + uptr = pointer to tape unit + count = count of files to skip + skipped = pointer to number of files actually skipped + recsskipped = pointer to number of records skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged, PNU set + end of file/medium unchanged, PNU set + tape mark updated + data record updated + data record error updated +*/ + +t_stat sim_tape_spfilebyrecf (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +t_stat st; +uint32 filerecsskipped; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecf(unit=%d, count=%d)\n", uptr-ctx->dptr->units, count); + +*skipped = 0; +*recsskipped = 0; +while (*skipped < count) { /* loopo */ + while (1) { + st = sim_tape_sprecsf (uptr, 0x1ffffff, &filerecsskipped);/* spc recs */ + *recsskipped += filerecsskipped; + if (st != MTSE_OK) + break; + } + if (st == MTSE_TMK) + *skipped = *skipped + 1; /* # files skipped */ + else + return st; + } +return MTSE_OK; +} + +t_stat sim_tape_spfilebyrecf_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_spfilebyrecf (uptr, count, skipped, recsskipped); +AIO_CALL(TOP_SPFF, NULL, skipped, recsskipped, 0, count, 0, 0, NULL, callback); +return r; +} + +/* Space files forward + + Inputs: + uptr = pointer to tape unit + count = count of files to skip + skipped = pointer to number of files actually skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged, PNU set + end of file/medium unchanged, PNU set + tape mark updated + data record updated + data record error updated +*/ + +t_stat sim_tape_spfilef (UNIT *uptr, uint32 count, uint32 *skipped) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +uint32 totalrecsskipped; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilef(unit=%d, count=%d)\n", uptr-ctx->dptr->units, count); + +return sim_tape_spfilebyrecf (uptr, count, skipped, &totalrecsskipped); +} + +t_stat sim_tape_spfilef_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_spfilef (uptr, count, skipped); +AIO_CALL(TOP_SPFF, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback); +return r; +} + +/* Space files reverse by record + + Inputs: + uptr = pointer to tape unit + count = count of files to skip + skipped = pointer to number of files actually skipped + recsskipped = pointer to number of records skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + beginning of tape unchanged + read error unchanged + end of file unchanged + end of medium updated + tape mark updated + data record updated +*/ + +t_stat sim_tape_spfilebyrecr (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +t_stat st; +uint32 filerecsskipped; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecr(unit=%d, count=%d)\n", uptr-ctx->dptr->units, count); + +*skipped = 0; +*recsskipped = 0; +while (*skipped < count) { /* loopo */ + while (1) { + st = sim_tape_sprecsr (uptr, 0x1ffffff, &filerecsskipped);/* spc recs rev */ + *recsskipped += filerecsskipped; + if (st != MTSE_OK) + break; + } + if (st == MTSE_TMK) + *skipped = *skipped + 1; /* # files skipped */ + else + return st; + } +return MTSE_OK; +} + +t_stat sim_tape_spfilebyrecr_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_spfilebyrecr (uptr, count, skipped, recsskipped); +AIO_CALL(TOP_SPFR, NULL, skipped, recsskipped, 0, count, 0, 0, NULL, callback); +return r; +} + +/* Space files reverse + + Inputs: + uptr = pointer to tape unit + count = count of files to skip + skipped = pointer to number of files actually skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + beginning of tape unchanged + read error unchanged + end of file unchanged + end of medium updated + tape mark updated + data record updated +*/ + +t_stat sim_tape_spfiler (UNIT *uptr, uint32 count, uint32 *skipped) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +uint32 totalrecsskipped; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfiler(unit=%d, count=%d)\n", uptr-ctx->dptr->units, count); + +return sim_tape_spfilebyrecr (uptr, count, skipped, &totalrecsskipped); +} + +t_stat sim_tape_spfiler_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_spfiler (uptr, count, skipped); +AIO_CALL(TOP_SPFR, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback); +return r; +} + /* Rewind tape */ t_stat sim_tape_rewind (UNIT *uptr) { +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; + +if (uptr->flags & UNIT_ATT) + sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rewind(unit=%d)\n", uptr-ctx->dptr->units); uptr->pos = 0; MT_CLR_PNU (uptr); return MTSE_OK; } +t_stat sim_tape_rewind_a (UNIT *uptr, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_rewind (uptr); +AIO_CALL(TOP_RWND, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); +return r; +} + +/* Position Tape */ + +t_stat sim_tape_position (UNIT *uptr, uint8 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +t_stat r = MTSE_OK; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_position(unit=%d, flags=0x%X, recs=%d, files=%d)\n", uptr-ctx->dptr->units, flags, recs, files); + +*recsskipped = *filesskipped = *objectsskipped = 0; +if (flags & MTPOS_M_REW) + r = sim_tape_rewind (uptr); +if (r != MTSE_OK) + return r; +if (flags & MTPOS_M_OBJ) { + uint32 objs = recs; + uint32 skipped; + uint32 objsremaining = objs; + + while (*objectsskipped < objs) { /* loopo */ + if (flags & MTPOS_M_REV) /* reverse? */ + r = sim_tape_sprecsr (uptr, objsremaining, &skipped); + else + r = sim_tape_sprecsf (uptr, objsremaining, &skipped); + objsremaining = objsremaining - (skipped + ((r == MTSE_TMK) ? 1 : 0)); + if ((r == MTSE_TMK) || (r == MTSE_OK)) + *objectsskipped = *objectsskipped + skipped + ((r == MTSE_TMK) ? 1 : 0); + else + return r; + } + r = MTSE_OK; + } +else { + uint32 fileskiprecs; + + if (flags & MTPOS_M_REV) /* reverse? */ + r = sim_tape_spfilebyrecr (uptr, files, filesskipped, &fileskiprecs); + else + r = sim_tape_spfilebyrecf (uptr, files, filesskipped, &fileskiprecs); + if (r != MTSE_OK) + return r; + if (flags & MTPOS_M_REV) /* reverse? */ + r = sim_tape_sprecsr (uptr, recs, recsskipped); + else + r = sim_tape_sprecsf (uptr, recs, recsskipped); + if (r == MTSE_TMK) + *filesskipped = *filesskipped + 1; + *objectsskipped = fileskiprecs + *filesskipped + *recsskipped; + } +return r; +} + +t_stat sim_tape_position_a (UNIT *uptr, uint8 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_position (uptr, flags, recs, recsskipped, files, filesskipped, objectsskipped); +AIO_CALL(TOP_POSN, NULL, recsskipped, filesskipped, 0, flags, recs, files, objectsskipped, callback); +return r; +} + /* Reset tape */ t_stat sim_tape_reset (UNIT *uptr) diff --git a/sim_tape.h b/sim_tape.h index 22eb4aac..f851ff6e 100644 --- a/sim_tape.h +++ b/sim_tape.h @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 05-Feb-11 MP Add Asynch I/O support 30-Aug-06 JDB Added erase gap support 14-Feb-06 RMS Added variable tape capacity 17-Dec-05 RMS Added write support for Paul Pierce 7b format @@ -91,6 +92,14 @@ typedef uint16 t_tpclnt; /* magtape rec lnt */ #define MT_TST_PNU(u) ((u)->flags & MTUF_PNU) #define MT_GET_FMT(u) (((u)->flags >> MTUF_V_FMT) & MTUF_M_FMT) +/* sim_tape_position Position Flags */ +#define MTPOS_V_REW 3 +#define MTPOS_M_REW (1u << MTPOS_V_REW) /* Rewind First */ +#define MTPOS_V_REV 2 +#define MTPOS_M_REV (1u << MTPOS_V_REV) /* Reverse Direction */ +#define MTPOS_V_OBJ 1 +#define MTPOS_M_OBJ (1u << MTPOS_V_OBJ) /* Objects vs Records/Files */ + /* Return status codes */ #define MTSE_OK 0 /* no error */ @@ -104,19 +113,47 @@ typedef uint16 t_tpclnt; /* magtape rec lnt */ #define MTSE_RECE 8 /* error in record */ #define MTSE_WRP 9 /* write protected */ +typedef void (*TAPE_PCALLBACK)(UNIT *unit, t_stat status); + /* Prototypes */ +t_stat sim_tape_attach_ex (UNIT *uptr, char *cptr, uint32 dbit); t_stat sim_tape_attach (UNIT *uptr, char *cptr); t_stat sim_tape_detach (UNIT *uptr); t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max); +t_stat sim_tape_rdrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback); t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max); +t_stat sim_tape_rdrecr_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback); t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc); +t_stat sim_tape_wrrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt bc, TAPE_PCALLBACK callback); t_stat sim_tape_wrtmk (UNIT *uptr); +t_stat sim_tape_wrtmk_a (UNIT *uptr, TAPE_PCALLBACK callback); t_stat sim_tape_wreom (UNIT *uptr); +t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback); +t_stat sim_tape_wreomrw (UNIT *uptr); +t_stat sim_tape_wreomrw_a (UNIT *uptr, TAPE_PCALLBACK callback); t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen, uint32 bpi); +t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, uint32 bpi, TAPE_PCALLBACK callback); t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc); +t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback); +t_stat sim_tape_sprecsf (UNIT *uptr, uint32 count, uint32 *skipped); +t_stat sim_tape_sprecsf_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback); +t_stat sim_tape_spfilef (UNIT *uptr, uint32 count, uint32 *skipped); +t_stat sim_tape_spfilef_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback); +t_stat sim_tape_spfilebyrecf (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped); +t_stat sim_tape_spfilebyrecf_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, TAPE_PCALLBACK callback); t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc); +t_stat sim_tape_sprecr_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback); +t_stat sim_tape_sprecsr (UNIT *uptr, uint32 count, uint32 *skipped); +t_stat sim_tape_sprecsr_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback); +t_stat sim_tape_spfiler (UNIT *uptr, uint32 count, uint32 *skipped); +t_stat sim_tape_spfiler_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback); +t_stat sim_tape_spfilebyrecr (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped); +t_stat sim_tape_spfilebyrecr_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, TAPE_PCALLBACK callback); t_stat sim_tape_rewind (UNIT *uptr); +t_stat sim_tape_rewind_a (UNIT *uptr, TAPE_PCALLBACK callback); +t_stat sim_tape_position (UNIT *uptr, uint8 flags, uint32 recs, uint32 *recskipped, uint32 files, uint32 *fileskipped, uint32 *objectsskipped); +t_stat sim_tape_position_a (UNIT *uptr, uint8 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped, TAPE_PCALLBACK callback); t_stat sim_tape_reset (UNIT *uptr); t_bool sim_tape_bot (UNIT *uptr); t_bool sim_tape_wrp (UNIT *uptr); @@ -125,5 +162,8 @@ t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat sim_tape_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat sim_tape_set_asynch (UNIT *uptr, int latency); +t_stat sim_tape_clr_asynch (UNIT *uptr); +void sim_tape_data_trace (UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason); #endif diff --git a/sim_timer.c b/sim_timer.c index af84fd48..f973a8c1 100644 --- a/sim_timer.c +++ b/sim_timer.c @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 05-Jan-11 MP Added Asynch I/O support 22-Sep-08 RMS Added "stability threshold" for idle routine 27-May-08 RMS Fixed bug in Linux idle routines (from Walter Mueller) 18-Jun-07 RMS Modified idle to exclude counted delays @@ -51,6 +52,7 @@ #include t_bool sim_idle_enab = FALSE; /* global flag */ +t_bool sim_idle_wait = FALSE; /* global flag */ static uint32 sim_idle_rate_ms = 0; static uint32 sim_idle_stable = SIM_IDLE_STDFLT; @@ -76,6 +78,11 @@ UNIT sim_throt_unit = { UDATA (&sim_throt_svc, 0, 0) }; #if defined (__VAX) #define sys$gettim SYS$GETTIM +#define sys$setimr SYS$SETIMR +#define lib$emul LIB$EMUL +#define sys$waitfr SYS$WAITFR +#define lib$subx LIB$SUBX +#define lib$ediv LIB$EDIV #endif #include @@ -138,10 +145,30 @@ sys$waitfr (2); return sim_os_msec () - stime; } -/* Win32 routines */ +#if defined(SIM_ASYNCH_IO) +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 1 +int clock_gettime(int clk_id, struct timespec *tp) +{ +uint32 secs, ns, tod[2], unixbase[2] = {0xd53e8000, 0x019db1de}; + +if (clk_id != CLOCK_REALTIME) + return -1; + +sys$gettim (tod); /* time 0.1usec */ +lib$subx(tod, unixbase, tod); /* convert to unix base */ +lib$ediv(&10000000, tod, &secs, &ns); /* isolate seconds & 100ns parts */ +tp->tv_sec = secs; +tp->tv_nsec = ns*100; +return 0; +} +#endif /* CLOCK_REALTIME */ +#endif /* SIM_ASYNCH_IO */ #elif defined (_WIN32) +/* Win32 routines */ + #include const t_bool rtc_avail = TRUE; @@ -192,10 +219,26 @@ Sleep (msec); return sim_os_msec () - stime; } -/* OS/2 routines, from Bruce Ray */ +#if !defined(CLOCK_REALTIME) && defined (SIM_ASYNCH_IO) +#define CLOCK_REALTIME 1 +int clock_gettime(int clk_id, struct timespec *tp) +{ +t_uint64 now, unixbase; + +unixbase = 116444736; +unixbase *= 1000000000; +GetSystemTimeAsFileTime((FILETIME*)&now); +now -= unixbase; +tp->tv_sec = (long)(now/10000000); +tp->tv_nsec = (now%10000000)*100; +return 0; +} +#endif #elif defined (__OS2__) +/* OS/2 routines, from Bruce Ray */ + const t_bool rtc_avail = FALSE; uint32 sim_os_msec () @@ -266,6 +309,22 @@ treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI; return sim_os_msec () - stime; } +#if !defined(CLOCK_REALTIME) && defined (SIM_ASYNCH_IO) +#define CLOCK_REALTIME 1 +int clock_gettime(int clk_id, struct timespec *tp) +{ +struct timeval cur; +struct timezone foo; + +if (clk_id != CLOCK_REALTIME) + return -1; +gettimeofday (&cur, &foo); +tp->tv_sec = cur.tv_sec; +tp->tv_nsec = cur.tv_usec*1000; +return 0; +} +#endif + #else /* UNIX routines */ @@ -298,19 +357,6 @@ return; uint32 sim_os_ms_sleep_init (void) { -#if defined (_POSIX_SOURCE) /* POSIX-compliant */ - -struct timespec treq; -uint32 msec; - -if (clock_getres (CLOCK_REALTIME, &treq) != 0) - return 0; -msec = (treq.tv_nsec + (NANOS_PER_MILLI - 1)) / NANOS_PER_MILLI; -if (msec > SIM_IDLE_MAX) return 0; -return msec; - -#else /* others */ - uint32 i, t1, t2, tot, tim; for (i = 0, tot = 0; i < sleep1Samples; i++) { @@ -320,15 +366,30 @@ for (i = 0, tot = 0; i < sleep1Samples; i++) { tot += (t2 - t1); } tim = (tot + (sleep1Samples - 1)) / sleep1Samples; -if (tim == 0) - tim = 1; -else if (tim > SIM_IDLE_MAX) +if (tim > SIM_IDLE_MAX) tim = 0; return tim; - -#endif } +#if !defined(_POSIX_SOURCE) && defined(SIM_ASYNCH_IO) +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 1 +typedef int clockid_t; +#endif +int clock_gettime(clockid_t clk_id, struct timespec *tp) +{ +struct timeval cur; +struct timezone foo; + +if (clk_id != CLOCK_REALTIME) + return -1; +gettimeofday (&cur, &foo); +tp->tv_sec = cur.tv_sec; +tp->tv_nsec = cur.tv_usec*1000; +return 0; +} +#endif + uint32 sim_os_ms_sleep (unsigned int milliseconds) { uint32 stime = sim_os_msec (); @@ -342,6 +403,31 @@ return sim_os_msec () - stime; #endif +#if defined(SIM_ASYNCH_IO) +uint32 sim_idle_ms_sleep (unsigned int msec) +{ +uint32 start_time = sim_os_msec(); +struct timespec done_time; + +clock_gettime(CLOCK_REALTIME, &done_time); +done_time.tv_sec += (msec/1000); +done_time.tv_nsec += 1000000*(msec%1000); +if (done_time.tv_nsec > 1000000000) { + done_time.tv_sec += 1; + done_time.tv_nsec = done_time.tv_nsec%1000000000; + } +pthread_mutex_lock (&sim_asynch_lock); +sim_idle_wait = TRUE; +pthread_cond_timedwait (&sim_asynch_wake, &sim_asynch_lock, &done_time); +sim_idle_wait = FALSE; +pthread_mutex_unlock (&sim_asynch_lock); +return sim_os_msec() - start_time; +} +#define SIM_IDLE_MS_SLEEP sim_idle_ms_sleep +#else +#define SIM_IDLE_MS_SLEEP sim_os_ms_sleep +#endif + /* OS independent clock calibration package */ static int32 rtc_ticks[SIM_NTIMERS] = { 0 }; /* ticks */ @@ -423,6 +509,7 @@ if (rtc_based[tmr] <= 0) /* never negative or zer rtc_based[tmr] = 1; if (rtc_currd[tmr] <= 0) /* never negative or zero! */ rtc_currd[tmr] = 1; +AIO_SET_INTERRUPT_LATENCY(rtc_currd[tmr]*ticksper); /* set interrrupt latency */ return rtc_currd[tmr]; } @@ -462,7 +549,8 @@ return (sim_idle_rate_ms != 0); t_bool sim_idle (uint32 tmr, t_bool sin_cyc) { -uint32 cyc_ms, w_ms, w_idle, act_ms; +static uint32 cyc_ms; +uint32 w_ms, w_idle, act_ms; int32 act_cyc; if ((sim_clock_queue == NULL) || /* clock queue empty? */ @@ -472,7 +560,8 @@ if ((sim_clock_queue == NULL) || /* clock queue empty? */ sim_interval = sim_interval - 1; return FALSE; } -cyc_ms = (rtc_currd[tmr] * rtc_hz[tmr]) / 1000; /* cycles per msec */ +if (!cyc_ms) + cyc_ms = (rtc_currd[tmr] * rtc_hz[tmr]) / 1000; /* cycles per msec */ if ((sim_idle_rate_ms == 0) || (cyc_ms == 0)) { /* not possible? */ if (sin_cyc) sim_interval = sim_interval - 1; @@ -485,11 +574,11 @@ if (w_idle == 0) { /* none? */ sim_interval = sim_interval - 1; return FALSE; } -act_ms = sim_os_ms_sleep (w_idle); /* wait */ +act_ms = SIM_IDLE_MS_SLEEP (w_ms); /* wait */ act_cyc = act_ms * cyc_ms; if (sim_interval > act_cyc) sim_interval = sim_interval - act_cyc; -else sim_interval = 1; +else sim_interval = 0; return TRUE; }