From 87c3e3452f6c1a0e19f333d3992f47ec227de9e5 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Fri, 15 Apr 2011 08:49:18 -0700 Subject: [PATCH 1/5] Added Asynch I/O and Disk Support for various Disk formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I’ve always wanted to have the option to have simulated devices behave more naturally with respect to I/O operations. By more naturally I mean that the current simulator model I/O is either polled (for asynchronous things link Muxes and Network), or it is performed in the middle of some instruction execution taking possibly many milliseconds (disk and/or tapes). The existing model creates quite deterministic behavior which helps to debug and understand issues, but it trades off potential instruction execution while performing these I/O operations in between instruction execution. To address this concept (while still retaining the potential advantages of the original model), I’ve designed an Asynch I/O model extension for simh. In order to flesh-out and debug this design, I’ve also refactored several devices to utilize this capability. Please read the attached 0readmeAsynchIO.txt file for concept details about the approach. In order to make disk devices easy to implement (within or without the AsynchIO framework), I’ve created a sim_disk.c library which is modeled on the sim_tape.c library to generalize disk I/O like tape I/O is generalized in sim_tape.c. This sim_disk.c library now provides that natural place to implement support for various disk implementation formats (just like sim_tape support several formats, and one day will be the place to add direct physical tape access). The current sim_disk library provides the framework for direct support of 3 different disk formats: 1) standard simh disk format 2) platform specific physical disk access and 3) platform independent Virtual Disk format. The Virtual Disk format is an implementation of the format described in the ”Microsoft Virtual Hard Disk (VHD) Image Format Specification”. The VHD specification is available for anyone to implement under the "Microsoft Open Specification Promise" described at http://www.microsoft.com/interop/osp/default.mspx. The VHD implementation includes support for: 1) Fixed sized disks 2) Dynamically expanding disks and 3) Differencing Disks. Dynamically expanding disks don’t change their “Virtual Size”, but they don’t consume disk space on the containing storage until the virtual sectors in the disk are actually written to (i.e. an RA81 or RA92 VHD with a VMS installed on it may initially only contain 30+ MB of files, and the resulting VHD will be 30+ MB). The VHD format contains meta data which describes the virtual device. Amongst this meta data is the simh device type which the VHD was originally created as. This metadata is therefore available whenever that VHD is attached to an emulated disk device in the future so the device type & size can be automatically be configured. Sim_disk_attach is used by device emulations to attach a simh/vhd/raw device to a simulated device. The following simh command switches are used by the sim_disk_attach API: -R Attach Read Only. -E Must Exist (if not specified an attempt to create the indicated virtual disk will be attempted). -F Open the indicated disk container in a specific format (default is to autodetect VHD defaulting to simh if the indicated container is not a VHD). -X When creating a VHD, create a fixed sized VHD (vs a Dynamically expanding one). -C Create a VHD and copy its contents from another disk (simh, VHD, or RAW format). -D Create a Differencing VHD (relative to an already existing VHD disk) Examples: sim> show rq RQ, address=20001468-2000146B*, no vector, 4 units RQ0, 159MB, not attached, write enabled, RD54, autosize, SIMH format RQ1, 159MB, not attached, write enabled, RD54, autosize, SIMH format RQ2, 159MB, not attached, write enabled, RD54, autosize, SIMH format RQ3, 409KB, not attached, write enabled, RX50, autosize, SIMH format sim> atta rq0 RA81.vhd sim> show rq0 RQ0, 456MB, attached to RA81.vhd, write enabled, RA81, autosize, VHD format sim> set rq2 ra92 sim> att rq2 -f vhd RA92.vhd RQ2: creating new file sim> sho rq2 RQ2, 1505MB, attached to RA92.vhd, write enabled, RA92, autosize, VHD format sim> ! dir RA92.vhd Volume in drive H is New Volume Volume Serial Number is F8DE-510C Directory of H:\Data 04/14/2011 12:57 PM 5,120 RA92.vhd 1 File(s) 5,120 bytes 0 Dir(s) 3,074,412,544 bytes free sim> atta rq3 -c RA92-1.vhd RA92.vhd sim> atta rq3 -c RA92-1.vhd RA92.vhd RQ3: creating new virtual disk 'RA92-1.vhd' RQ3: Copied 1505MB. 99% complete. RQ3: Copied 1505MB. Done. sim> sh rq3 RQ3, 1505MB, attached to RA92-1.vhd, write enabled, RA92, autosize, VHD format sim> ! dir RA92* Volume in drive H is New Volume Volume Serial Number is F8DE-510C Directory of H:\Data 04/14/2011 01:12 PM 5,120 RA92-1.vhd 04/14/2011 12:58 PM 5,120 RA92.vhd 2 File(s) 10,240 bytes 0 Dir(s) 3,074,404,352 bytes free sim> sho rq2 RQ2, 1505MB, not attached, write enabled, RA92, autosize, VHD format sim> set rq2 ra81 sim> set rq2 noauto sim> sho rq2 RQ2, 456MB, not attached, write enabled, RA81, noautosize, VHD format sim> set rq2 format=simh sim> sho rq2 RQ2, 456MB, not attached, write enabled, RA81, noautosize, SIMH format sim> atta rq2 -c RA81-Copy.vhd VMS055.dsk RQ2: creating new virtual disk 'RA81-Copy.vhd' RQ2: Copied 456MB. 99% complete. RQ2: Copied 456MB. Done. sim> sho rq2 RQ2, 456MB, attached to RA81-Copy.vhd, write enabled, RA81, noautosize, VHD format sim> det rq2 sim> ! dir RA81-Copy.vhd Volume in drive H is New Volume Volume Serial Number is F8DE-510C Directory of H:\Data 04/14/2011 01:22 PM 178,304,512 RA81-Copy.vhd 1 File(s) 178,304,512 bytes 0 Dir(s) 2,896,097,280 bytes free sim> ! dir VMS055.dsk Volume in drive H is New Volume Volume Serial Number is F8DE-510C Directory of H:\Data 03/08/2011 01:42 PM 403,663,872 VMS055.dsk 1 File(s) 403,663,872 bytes 0 Dir(s) 2,896,097,280 bytes free sim> --- 0readmeAsynchIO.txt | 161 +++ PDP11/pdp11_mscp.h | 1 + PDP11/pdp11_rp.c | 242 ++-- PDP11/pdp11_rq.c | 367 +++-- PDP11/pdp11_tq.c | 528 ++++--- VAX/vax780_sbi.c | 47 +- VAX/vax780_stddev.c | 5 + VAX/vax_cpu.c | 3 + VAX/vax_stddev.c | 5 + scp.c | 104 +- scp.h | 8 + sim_defs.h | 171 ++- sim_disk.c | 3272 +++++++++++++++++++++++++++++++++++++++++++ sim_disk.h | 86 ++ sim_fio.c | 160 ++- sim_fio.h | 7 + sim_tape.c | 855 ++++++++++- sim_tape.h | 40 + sim_timer.c | 137 +- 19 files changed, 5695 insertions(+), 504 deletions(-) create mode 100644 0readmeAsynchIO.txt create mode 100644 sim_disk.c create mode 100644 sim_disk.h 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 + ones 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 ones + 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; } From a1930c9b051a2d7e9ffb27569ca6ef8f858ccf8f Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Fri, 15 Apr 2011 08:51:11 -0700 Subject: [PATCH 2/5] Added Visual Studio Projects for VS2008 and VS2010 --- Visual Studio Projects/0ReadMe_Projects.txt | 33 ++ Visual Studio Projects/ALTAIR.vcproj | 295 ++++++++++++ Visual Studio Projects/AltairZ80.vcproj | 410 +++++++++++++++++ Visual Studio Projects/ECLIPSE.vcproj | 322 ++++++++++++++ Visual Studio Projects/GRI.vcproj | 290 ++++++++++++ Visual Studio Projects/H316.vcproj | 306 +++++++++++++ Visual Studio Projects/HP2100.vcproj | 398 +++++++++++++++++ Visual Studio Projects/I1401.vcproj | 310 +++++++++++++ Visual Studio Projects/I1620.vcproj | 310 +++++++++++++ Visual Studio Projects/I7094.vcproj | 330 ++++++++++++++ Visual Studio Projects/IBM1130.vcproj | 354 +++++++++++++++ Visual Studio Projects/ID16.vcproj | 338 ++++++++++++++ Visual Studio Projects/ID32.vcproj | 338 ++++++++++++++ Visual Studio Projects/NOVA.vcproj | 326 ++++++++++++++ Visual Studio Projects/PDP1.vcproj | 310 +++++++++++++ Visual Studio Projects/PDP10.vcproj | 344 ++++++++++++++ Visual Studio Projects/PDP11.vcproj | 468 ++++++++++++++++++++ Visual Studio Projects/PDP15.vcproj | 322 ++++++++++++++ Visual Studio Projects/PDP18B.vcproj | 326 ++++++++++++++ Visual Studio Projects/PDP4.vcproj | 322 ++++++++++++++ Visual Studio Projects/PDP7.vcproj | 322 ++++++++++++++ Visual Studio Projects/PDP8.vcproj | 350 +++++++++++++++ Visual Studio Projects/PDP9.vcproj | 326 ++++++++++++++ Visual Studio Projects/S3.vcproj | 302 +++++++++++++ Visual Studio Projects/SDS.vcproj | 318 +++++++++++++ Visual Studio Projects/SWTP.vcproj | 294 ++++++++++++ Visual Studio Projects/Simh.sln | 182 ++++++++ Visual Studio Projects/VAX.vcproj | 421 ++++++++++++++++++ Visual Studio Projects/VAX780.vcproj | 443 ++++++++++++++++++ Visual Studio Projects/lgp.vcproj | 290 ++++++++++++ 30 files changed, 9700 insertions(+) create mode 100644 Visual Studio Projects/0ReadMe_Projects.txt create mode 100644 Visual Studio Projects/ALTAIR.vcproj create mode 100644 Visual Studio Projects/AltairZ80.vcproj create mode 100644 Visual Studio Projects/ECLIPSE.vcproj create mode 100644 Visual Studio Projects/GRI.vcproj create mode 100644 Visual Studio Projects/H316.vcproj create mode 100644 Visual Studio Projects/HP2100.vcproj create mode 100644 Visual Studio Projects/I1401.vcproj create mode 100644 Visual Studio Projects/I1620.vcproj create mode 100644 Visual Studio Projects/I7094.vcproj create mode 100644 Visual Studio Projects/IBM1130.vcproj create mode 100644 Visual Studio Projects/ID16.vcproj create mode 100644 Visual Studio Projects/ID32.vcproj create mode 100644 Visual Studio Projects/NOVA.vcproj create mode 100644 Visual Studio Projects/PDP1.vcproj create mode 100644 Visual Studio Projects/PDP10.vcproj create mode 100644 Visual Studio Projects/PDP11.vcproj create mode 100644 Visual Studio Projects/PDP15.vcproj create mode 100644 Visual Studio Projects/PDP18B.vcproj create mode 100644 Visual Studio Projects/PDP4.vcproj create mode 100644 Visual Studio Projects/PDP7.vcproj create mode 100644 Visual Studio Projects/PDP8.vcproj create mode 100644 Visual Studio Projects/PDP9.vcproj create mode 100644 Visual Studio Projects/S3.vcproj create mode 100644 Visual Studio Projects/SDS.vcproj create mode 100644 Visual Studio Projects/SWTP.vcproj create mode 100644 Visual Studio Projects/Simh.sln create mode 100644 Visual Studio Projects/VAX.vcproj create mode 100644 Visual Studio Projects/VAX780.vcproj create mode 100644 Visual Studio Projects/lgp.vcproj diff --git a/Visual Studio Projects/0ReadMe_Projects.txt b/Visual Studio Projects/0ReadMe_Projects.txt new file mode 100644 index 00000000..fa304799 --- /dev/null +++ b/Visual Studio Projects/0ReadMe_Projects.txt @@ -0,0 +1,33 @@ +This dirctory contains a set of Visual Studio 2008 build projects for the current simh code base. When used (with Visual Studio Express 2008 or Visual Studio Express 2010) it populates a directory tree under the BIN directory of the Simh distribution for temporary build files and produces resulting executables in the BIN/NT/Win32-Debug or BIN/NT/Win32-Release directories (depending on whether you target a Debug or Release build). It expects that a winpcap developer pack zip file is expanded in a directory parallel to the simh directory. + +For Example, the directory structure should look like: + + .../simh/simhv38-2-rc1/VAX/vax_cpu.c + .../simh/simhv38-2-rc1/scp.c + .../simh/simhv38-2-rc1/Visual Studio Projects/simh.sln + .../simh/simhv38-2-rc1/Visual Studio Projects/VAX.vcproj + .../simh/simhv38-2-rc1/BIN/Nt/Win32-Release/vax.exe + .../simh/winpcap/WpdPack/Include/pcap.h + + +The winpcap developer pack can be found at: + http://www.winpcap.org/devel.htm + + +Some features can be enabled if the pthreads API is available and contained also in a parallel place in the directory structure. + + + .../simh/pthreads/Pre-built.2/include/include/pthreads.h + + +To install pthreads API, create the directory: + + .../simh/pthreads/ + +download the file: ftp://sourceware.org/pub/pthreads-win32/pthreads-w32-2-8-0-release.exe +to that directory and execute it. Click on the Extract button. +Once installed, When running a simulator with pthreads support enabled, you will need a copy of the DLL file (simh\pthreads\Pre-built.2\lib\pthreadVC2.dll) to exist in either the %windir%\System32 directory or your working directory while running a simh simulator. The default working directory for included project files is the "Visual Studio Projects" directory. + + +Only network devices are capable of using pthreads to enhance their performance. Build the desire simulator with USE_READER_THREAD defined. The relevant simulators which have network support are VAX, VAX780, PDP11 and PDP10. + diff --git a/Visual Studio Projects/ALTAIR.vcproj b/Visual Studio Projects/ALTAIR.vcproj new file mode 100644 index 00000000..59fa3bd7 --- /dev/null +++ b/Visual Studio Projects/ALTAIR.vcproj @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/AltairZ80.vcproj b/Visual Studio Projects/AltairZ80.vcproj new file mode 100644 index 00000000..b5681a54 --- /dev/null +++ b/Visual Studio Projects/AltairZ80.vcproj @@ -0,0 +1,410 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/ECLIPSE.vcproj b/Visual Studio Projects/ECLIPSE.vcproj new file mode 100644 index 00000000..d92daee7 --- /dev/null +++ b/Visual Studio Projects/ECLIPSE.vcproj @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/GRI.vcproj b/Visual Studio Projects/GRI.vcproj new file mode 100644 index 00000000..811cd8aa --- /dev/null +++ b/Visual Studio Projects/GRI.vcproj @@ -0,0 +1,290 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/H316.vcproj b/Visual Studio Projects/H316.vcproj new file mode 100644 index 00000000..226ea7a8 --- /dev/null +++ b/Visual Studio Projects/H316.vcproj @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/HP2100.vcproj b/Visual Studio Projects/HP2100.vcproj new file mode 100644 index 00000000..052039da --- /dev/null +++ b/Visual Studio Projects/HP2100.vcproj @@ -0,0 +1,398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/I1401.vcproj b/Visual Studio Projects/I1401.vcproj new file mode 100644 index 00000000..d1f8f561 --- /dev/null +++ b/Visual Studio Projects/I1401.vcproj @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/I1620.vcproj b/Visual Studio Projects/I1620.vcproj new file mode 100644 index 00000000..f24ac970 --- /dev/null +++ b/Visual Studio Projects/I1620.vcproj @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/I7094.vcproj b/Visual Studio Projects/I7094.vcproj new file mode 100644 index 00000000..c96b4579 --- /dev/null +++ b/Visual Studio Projects/I7094.vcproj @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/IBM1130.vcproj b/Visual Studio Projects/IBM1130.vcproj new file mode 100644 index 00000000..cec64ded --- /dev/null +++ b/Visual Studio Projects/IBM1130.vcproj @@ -0,0 +1,354 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/ID16.vcproj b/Visual Studio Projects/ID16.vcproj new file mode 100644 index 00000000..162a6fac --- /dev/null +++ b/Visual Studio Projects/ID16.vcproj @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/ID32.vcproj b/Visual Studio Projects/ID32.vcproj new file mode 100644 index 00000000..237c2e7c --- /dev/null +++ b/Visual Studio Projects/ID32.vcproj @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/NOVA.vcproj b/Visual Studio Projects/NOVA.vcproj new file mode 100644 index 00000000..22cb8bc2 --- /dev/null +++ b/Visual Studio Projects/NOVA.vcproj @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/PDP1.vcproj b/Visual Studio Projects/PDP1.vcproj new file mode 100644 index 00000000..57431cb4 --- /dev/null +++ b/Visual Studio Projects/PDP1.vcproj @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/PDP10.vcproj b/Visual Studio Projects/PDP10.vcproj new file mode 100644 index 00000000..eff6841a --- /dev/null +++ b/Visual Studio Projects/PDP10.vcproj @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/PDP11.vcproj b/Visual Studio Projects/PDP11.vcproj new file mode 100644 index 00000000..30e71620 --- /dev/null +++ b/Visual Studio Projects/PDP11.vcproj @@ -0,0 +1,468 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/PDP15.vcproj b/Visual Studio Projects/PDP15.vcproj new file mode 100644 index 00000000..fb326065 --- /dev/null +++ b/Visual Studio Projects/PDP15.vcproj @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/PDP18B.vcproj b/Visual Studio Projects/PDP18B.vcproj new file mode 100644 index 00000000..5d829a55 --- /dev/null +++ b/Visual Studio Projects/PDP18B.vcproj @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/PDP4.vcproj b/Visual Studio Projects/PDP4.vcproj new file mode 100644 index 00000000..3a5fa23d --- /dev/null +++ b/Visual Studio Projects/PDP4.vcproj @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/PDP7.vcproj b/Visual Studio Projects/PDP7.vcproj new file mode 100644 index 00000000..dce8d70e --- /dev/null +++ b/Visual Studio Projects/PDP7.vcproj @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/PDP8.vcproj b/Visual Studio Projects/PDP8.vcproj new file mode 100644 index 00000000..26335916 --- /dev/null +++ b/Visual Studio Projects/PDP8.vcproj @@ -0,0 +1,350 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/PDP9.vcproj b/Visual Studio Projects/PDP9.vcproj new file mode 100644 index 00000000..7bec5514 --- /dev/null +++ b/Visual Studio Projects/PDP9.vcproj @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/S3.vcproj b/Visual Studio Projects/S3.vcproj new file mode 100644 index 00000000..fba7a787 --- /dev/null +++ b/Visual Studio Projects/S3.vcproj @@ -0,0 +1,302 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/SDS.vcproj b/Visual Studio Projects/SDS.vcproj new file mode 100644 index 00000000..d1c3ff9c --- /dev/null +++ b/Visual Studio Projects/SDS.vcproj @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/SWTP.vcproj b/Visual Studio Projects/SWTP.vcproj new file mode 100644 index 00000000..ca490ae2 --- /dev/null +++ b/Visual Studio Projects/SWTP.vcproj @@ -0,0 +1,294 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/Simh.sln b/Visual Studio Projects/Simh.sln new file mode 100644 index 00000000..0b5fe2da --- /dev/null +++ b/Visual Studio Projects/Simh.sln @@ -0,0 +1,182 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VAX", "VAX.vcproj", "{D5D873F7-D286-43E7-958A-3D838FAA0856}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VAX780", "VAX780.vcproj", "{D5D873F7-D286-43E7-958A-3D83DEADBEEF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AltairZ80", "AltairZ80.vcproj", "{BC7F37AD-7414-43C3-829D-214CD1113D67}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Altair", "ALTAIR.vcproj", "{1C602310-C406-4446-85C3-E5AE0E836120}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GRI", "GRI.vcproj", "{611E140C-8403-4FD8-AF53-CE01C8452B34}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HP2100", "HP2100.vcproj", "{7A9428F5-593C-4217-B8FE-980E249D3DB9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "H316", "H316.vcproj", "{C915B408-80D8-4925-BF7B-0469436B33BF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ID16", "ID16.vcproj", "{D90C77B3-A3E7-40D3-BB88-18A4EF1C001D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ID32", "ID32.vcproj", "{324EF17B-1683-48B5-824D-FACF17AEB27B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PDP8", "PDP8.vcproj", "{5EB65E13-1E6A-46A4-B7FE-EC87F8702067}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PDP10", "PDP10.vcproj", "{A39C0AFE-BDE5-4236-B740-AC710FCA1DA2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "S3", "S3.vcproj", "{927C3BD9-BD0C-4A23-99F9-573A40236509}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDS", "SDS.vcproj", "{750762C6-A2AF-40BA-A006-5E68002C1E87}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PDP11", "PDP11.vcproj", "{49499E1B-5019-4B98-9DC7-2E73306D5578}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PDP1", "PDP1.vcproj", "{CB017838-5DC5-4B9D-A8F7-7B36AA4A3331}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "I1401", "I1401.vcproj", "{C92737AD-07CC-492F-AA76-D169CEF5BBAB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "I1620", "I1620.vcproj", "{089C9C0B-C4F7-4923-86C4-F14BF5D61821}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IBM1130", "IBM1130.vcproj", "{D593C954-5115-4D15-ABDB-01B66006FF6F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PDP18B", "PDP18B.vcproj", "{0A3FD54C-E497-4B2D-AD32-D83EAF996D59}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PDP15", "PDP15.vcproj", "{44C07AA4-6D56-45ED-8393-18A23E76B758}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PDP9", "PDP9.vcproj", "{9D589BCA-9E10-4FFA-B43F-DDFA91C1C098}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PDP7", "PDP7.vcproj", "{0F8B9E39-56A7-45BE-A68F-04F7EB8EF8A3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PDP4", "PDP4.vcproj", "{C51841F3-BD47-41C3-946C-20F893FB5A23}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NOVA", "NOVA.vcproj", "{9B55ACBB-C29A-40EB-98BF-D1047912389E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ECLIPSE", "ECLIPSE.vcproj", "{FF632F3D-9F62-481D-A5C7-AD090F46143C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lgp", "lgp.vcproj", "{927C3BD9-BD0C-4A23-99F9-5ABC40236509}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "I7094", "I7094.vcproj", "{927C3BD9-BD0C-4A23-99F9-DEAD402BEEF9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SWTP", "SWTP.vcproj", "{0ABAF350-853E-4A8F-8435-B583E29FB78C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D5D873F7-D286-43E7-958A-3D838FAA0856}.Debug|Win32.ActiveCfg = Debug|Win32 + {D5D873F7-D286-43E7-958A-3D838FAA0856}.Debug|Win32.Build.0 = Debug|Win32 + {D5D873F7-D286-43E7-958A-3D838FAA0856}.Release|Win32.ActiveCfg = Release|Win32 + {D5D873F7-D286-43E7-958A-3D838FAA0856}.Release|Win32.Build.0 = Release|Win32 + {D5D873F7-D286-43E7-958A-3D83DEADBEEF}.Debug|Win32.ActiveCfg = Debug|Win32 + {D5D873F7-D286-43E7-958A-3D83DEADBEEF}.Debug|Win32.Build.0 = Debug|Win32 + {D5D873F7-D286-43E7-958A-3D83DEADBEEF}.Release|Win32.ActiveCfg = Release|Win32 + {D5D873F7-D286-43E7-958A-3D83DEADBEEF}.Release|Win32.Build.0 = Release|Win32 + {BC7F37AD-7414-43C3-829D-214CD1113D67}.Debug|Win32.ActiveCfg = Debug|Win32 + {BC7F37AD-7414-43C3-829D-214CD1113D67}.Debug|Win32.Build.0 = Debug|Win32 + {BC7F37AD-7414-43C3-829D-214CD1113D67}.Release|Win32.ActiveCfg = Release|Win32 + {BC7F37AD-7414-43C3-829D-214CD1113D67}.Release|Win32.Build.0 = Release|Win32 + {1C602310-C406-4446-85C3-E5AE0E836120}.Debug|Win32.ActiveCfg = Debug|Win32 + {1C602310-C406-4446-85C3-E5AE0E836120}.Debug|Win32.Build.0 = Debug|Win32 + {1C602310-C406-4446-85C3-E5AE0E836120}.Release|Win32.ActiveCfg = Release|Win32 + {1C602310-C406-4446-85C3-E5AE0E836120}.Release|Win32.Build.0 = Release|Win32 + {611E140C-8403-4FD8-AF53-CE01C8452B34}.Debug|Win32.ActiveCfg = Debug|Win32 + {611E140C-8403-4FD8-AF53-CE01C8452B34}.Debug|Win32.Build.0 = Debug|Win32 + {611E140C-8403-4FD8-AF53-CE01C8452B34}.Release|Win32.ActiveCfg = Release|Win32 + {611E140C-8403-4FD8-AF53-CE01C8452B34}.Release|Win32.Build.0 = Release|Win32 + {7A9428F5-593C-4217-B8FE-980E249D3DB9}.Debug|Win32.ActiveCfg = Debug|Win32 + {7A9428F5-593C-4217-B8FE-980E249D3DB9}.Debug|Win32.Build.0 = Debug|Win32 + {7A9428F5-593C-4217-B8FE-980E249D3DB9}.Release|Win32.ActiveCfg = Release|Win32 + {7A9428F5-593C-4217-B8FE-980E249D3DB9}.Release|Win32.Build.0 = Release|Win32 + {C915B408-80D8-4925-BF7B-0469436B33BF}.Debug|Win32.ActiveCfg = Debug|Win32 + {C915B408-80D8-4925-BF7B-0469436B33BF}.Debug|Win32.Build.0 = Debug|Win32 + {C915B408-80D8-4925-BF7B-0469436B33BF}.Release|Win32.ActiveCfg = Release|Win32 + {C915B408-80D8-4925-BF7B-0469436B33BF}.Release|Win32.Build.0 = Release|Win32 + {D90C77B3-A3E7-40D3-BB88-18A4EF1C001D}.Debug|Win32.ActiveCfg = Debug|Win32 + {D90C77B3-A3E7-40D3-BB88-18A4EF1C001D}.Debug|Win32.Build.0 = Debug|Win32 + {D90C77B3-A3E7-40D3-BB88-18A4EF1C001D}.Release|Win32.ActiveCfg = Release|Win32 + {D90C77B3-A3E7-40D3-BB88-18A4EF1C001D}.Release|Win32.Build.0 = Release|Win32 + {324EF17B-1683-48B5-824D-FACF17AEB27B}.Debug|Win32.ActiveCfg = Debug|Win32 + {324EF17B-1683-48B5-824D-FACF17AEB27B}.Debug|Win32.Build.0 = Debug|Win32 + {324EF17B-1683-48B5-824D-FACF17AEB27B}.Release|Win32.ActiveCfg = Release|Win32 + {324EF17B-1683-48B5-824D-FACF17AEB27B}.Release|Win32.Build.0 = Release|Win32 + {5EB65E13-1E6A-46A4-B7FE-EC87F8702067}.Debug|Win32.ActiveCfg = Debug|Win32 + {5EB65E13-1E6A-46A4-B7FE-EC87F8702067}.Debug|Win32.Build.0 = Debug|Win32 + {5EB65E13-1E6A-46A4-B7FE-EC87F8702067}.Release|Win32.ActiveCfg = Release|Win32 + {5EB65E13-1E6A-46A4-B7FE-EC87F8702067}.Release|Win32.Build.0 = Release|Win32 + {A39C0AFE-BDE5-4236-B740-AC710FCA1DA2}.Debug|Win32.ActiveCfg = Debug|Win32 + {A39C0AFE-BDE5-4236-B740-AC710FCA1DA2}.Debug|Win32.Build.0 = Debug|Win32 + {A39C0AFE-BDE5-4236-B740-AC710FCA1DA2}.Release|Win32.ActiveCfg = Release|Win32 + {A39C0AFE-BDE5-4236-B740-AC710FCA1DA2}.Release|Win32.Build.0 = Release|Win32 + {927C3BD9-BD0C-4A23-99F9-573A40236509}.Debug|Win32.ActiveCfg = Debug|Win32 + {927C3BD9-BD0C-4A23-99F9-573A40236509}.Debug|Win32.Build.0 = Debug|Win32 + {927C3BD9-BD0C-4A23-99F9-573A40236509}.Release|Win32.ActiveCfg = Release|Win32 + {927C3BD9-BD0C-4A23-99F9-573A40236509}.Release|Win32.Build.0 = Release|Win32 + {750762C6-A2AF-40BA-A006-5E68002C1E87}.Debug|Win32.ActiveCfg = Debug|Win32 + {750762C6-A2AF-40BA-A006-5E68002C1E87}.Debug|Win32.Build.0 = Debug|Win32 + {750762C6-A2AF-40BA-A006-5E68002C1E87}.Release|Win32.ActiveCfg = Release|Win32 + {750762C6-A2AF-40BA-A006-5E68002C1E87}.Release|Win32.Build.0 = Release|Win32 + {49499E1B-5019-4B98-9DC7-2E73306D5578}.Debug|Win32.ActiveCfg = Debug|Win32 + {49499E1B-5019-4B98-9DC7-2E73306D5578}.Debug|Win32.Build.0 = Debug|Win32 + {49499E1B-5019-4B98-9DC7-2E73306D5578}.Release|Win32.ActiveCfg = Release|Win32 + {49499E1B-5019-4B98-9DC7-2E73306D5578}.Release|Win32.Build.0 = Release|Win32 + {CB017838-5DC5-4B9D-A8F7-7B36AA4A3331}.Debug|Win32.ActiveCfg = Debug|Win32 + {CB017838-5DC5-4B9D-A8F7-7B36AA4A3331}.Debug|Win32.Build.0 = Debug|Win32 + {CB017838-5DC5-4B9D-A8F7-7B36AA4A3331}.Release|Win32.ActiveCfg = Release|Win32 + {CB017838-5DC5-4B9D-A8F7-7B36AA4A3331}.Release|Win32.Build.0 = Release|Win32 + {C92737AD-07CC-492F-AA76-D169CEF5BBAB}.Debug|Win32.ActiveCfg = Debug|Win32 + {C92737AD-07CC-492F-AA76-D169CEF5BBAB}.Debug|Win32.Build.0 = Debug|Win32 + {C92737AD-07CC-492F-AA76-D169CEF5BBAB}.Release|Win32.ActiveCfg = Release|Win32 + {C92737AD-07CC-492F-AA76-D169CEF5BBAB}.Release|Win32.Build.0 = Release|Win32 + {089C9C0B-C4F7-4923-86C4-F14BF5D61821}.Debug|Win32.ActiveCfg = Debug|Win32 + {089C9C0B-C4F7-4923-86C4-F14BF5D61821}.Debug|Win32.Build.0 = Debug|Win32 + {089C9C0B-C4F7-4923-86C4-F14BF5D61821}.Release|Win32.ActiveCfg = Release|Win32 + {089C9C0B-C4F7-4923-86C4-F14BF5D61821}.Release|Win32.Build.0 = Release|Win32 + {D593C954-5115-4D15-ABDB-01B66006FF6F}.Debug|Win32.ActiveCfg = Debug|Win32 + {D593C954-5115-4D15-ABDB-01B66006FF6F}.Debug|Win32.Build.0 = Debug|Win32 + {D593C954-5115-4D15-ABDB-01B66006FF6F}.Release|Win32.ActiveCfg = Release|Win32 + {D593C954-5115-4D15-ABDB-01B66006FF6F}.Release|Win32.Build.0 = Release|Win32 + {0A3FD54C-E497-4B2D-AD32-D83EAF996D59}.Debug|Win32.ActiveCfg = Debug|Win32 + {0A3FD54C-E497-4B2D-AD32-D83EAF996D59}.Debug|Win32.Build.0 = Debug|Win32 + {0A3FD54C-E497-4B2D-AD32-D83EAF996D59}.Release|Win32.ActiveCfg = Release|Win32 + {0A3FD54C-E497-4B2D-AD32-D83EAF996D59}.Release|Win32.Build.0 = Release|Win32 + {44C07AA4-6D56-45ED-8393-18A23E76B758}.Debug|Win32.ActiveCfg = Debug|Win32 + {44C07AA4-6D56-45ED-8393-18A23E76B758}.Debug|Win32.Build.0 = Debug|Win32 + {44C07AA4-6D56-45ED-8393-18A23E76B758}.Release|Win32.ActiveCfg = Release|Win32 + {44C07AA4-6D56-45ED-8393-18A23E76B758}.Release|Win32.Build.0 = Release|Win32 + {9D589BCA-9E10-4FFA-B43F-DDFA91C1C098}.Debug|Win32.ActiveCfg = Debug|Win32 + {9D589BCA-9E10-4FFA-B43F-DDFA91C1C098}.Debug|Win32.Build.0 = Debug|Win32 + {9D589BCA-9E10-4FFA-B43F-DDFA91C1C098}.Release|Win32.ActiveCfg = Release|Win32 + {9D589BCA-9E10-4FFA-B43F-DDFA91C1C098}.Release|Win32.Build.0 = Release|Win32 + {0F8B9E39-56A7-45BE-A68F-04F7EB8EF8A3}.Debug|Win32.ActiveCfg = Debug|Win32 + {0F8B9E39-56A7-45BE-A68F-04F7EB8EF8A3}.Debug|Win32.Build.0 = Debug|Win32 + {0F8B9E39-56A7-45BE-A68F-04F7EB8EF8A3}.Release|Win32.ActiveCfg = Release|Win32 + {0F8B9E39-56A7-45BE-A68F-04F7EB8EF8A3}.Release|Win32.Build.0 = Release|Win32 + {C51841F3-BD47-41C3-946C-20F893FB5A23}.Debug|Win32.ActiveCfg = Debug|Win32 + {C51841F3-BD47-41C3-946C-20F893FB5A23}.Debug|Win32.Build.0 = Debug|Win32 + {C51841F3-BD47-41C3-946C-20F893FB5A23}.Release|Win32.ActiveCfg = Release|Win32 + {C51841F3-BD47-41C3-946C-20F893FB5A23}.Release|Win32.Build.0 = Release|Win32 + {9B55ACBB-C29A-40EB-98BF-D1047912389E}.Debug|Win32.ActiveCfg = Debug|Win32 + {9B55ACBB-C29A-40EB-98BF-D1047912389E}.Debug|Win32.Build.0 = Debug|Win32 + {9B55ACBB-C29A-40EB-98BF-D1047912389E}.Release|Win32.ActiveCfg = Release|Win32 + {9B55ACBB-C29A-40EB-98BF-D1047912389E}.Release|Win32.Build.0 = Release|Win32 + {FF632F3D-9F62-481D-A5C7-AD090F46143C}.Debug|Win32.ActiveCfg = Debug|Win32 + {FF632F3D-9F62-481D-A5C7-AD090F46143C}.Debug|Win32.Build.0 = Debug|Win32 + {FF632F3D-9F62-481D-A5C7-AD090F46143C}.Release|Win32.ActiveCfg = Release|Win32 + {FF632F3D-9F62-481D-A5C7-AD090F46143C}.Release|Win32.Build.0 = Release|Win32 + {927C3BD9-BD0C-4A23-99F9-5ABC40236509}.Debug|Win32.ActiveCfg = Debug|Win32 + {927C3BD9-BD0C-4A23-99F9-5ABC40236509}.Debug|Win32.Build.0 = Debug|Win32 + {927C3BD9-BD0C-4A23-99F9-5ABC40236509}.Release|Win32.ActiveCfg = Release|Win32 + {927C3BD9-BD0C-4A23-99F9-5ABC40236509}.Release|Win32.Build.0 = Release|Win32 + {927C3BD9-BD0C-4A23-99F9-DEAD402BEEF9}.Debug|Win32.ActiveCfg = Debug|Win32 + {927C3BD9-BD0C-4A23-99F9-DEAD402BEEF9}.Debug|Win32.Build.0 = Debug|Win32 + {927C3BD9-BD0C-4A23-99F9-DEAD402BEEF9}.Release|Win32.ActiveCfg = Release|Win32 + {927C3BD9-BD0C-4A23-99F9-DEAD402BEEF9}.Release|Win32.Build.0 = Release|Win32 + {0ABAF350-853E-4A8F-8435-B583E29FB78C}.Debug|Win32.ActiveCfg = Debug|Win32 + {0ABAF350-853E-4A8F-8435-B583E29FB78C}.Debug|Win32.Build.0 = Debug|Win32 + {0ABAF350-853E-4A8F-8435-B583E29FB78C}.Release|Win32.ActiveCfg = Release|Win32 + {0ABAF350-853E-4A8F-8435-B583E29FB78C}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Visual Studio Projects/VAX.vcproj b/Visual Studio Projects/VAX.vcproj new file mode 100644 index 00000000..3c980a12 --- /dev/null +++ b/Visual Studio Projects/VAX.vcproj @@ -0,0 +1,421 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/VAX780.vcproj b/Visual Studio Projects/VAX780.vcproj new file mode 100644 index 00000000..7ce43838 --- /dev/null +++ b/Visual Studio Projects/VAX780.vcproj @@ -0,0 +1,443 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/lgp.vcproj b/Visual Studio Projects/lgp.vcproj new file mode 100644 index 00000000..cf6d7156 --- /dev/null +++ b/Visual Studio Projects/lgp.vcproj @@ -0,0 +1,290 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From eb514e2a8d97b25bb77a20e102dfa8c9eca05b9c Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Fri, 15 Apr 2011 08:53:01 -0700 Subject: [PATCH 3/5] Merged in new ethernet code to potentially leverage AsynchIO --- 0readme_ethernet.txt | 387 +++++++-- PDP11/pdp11_xq.c | 1032 ++++++++++++++++++++---- PDP11/pdp11_xq.h | 187 ++++- PDP11/pdp11_xu.c | 180 ++++- PDP11/pdp11_xu.h | 9 +- sim_ether.c | 1814 ++++++++++++++++++++++++++++++++++-------- sim_ether.h | 77 +- 7 files changed, 3081 insertions(+), 605 deletions(-) diff --git a/0readme_ethernet.txt b/0readme_ethernet.txt index 4cc74fb3..9c8c7401 100644 --- a/0readme_ethernet.txt +++ b/0readme_ethernet.txt @@ -2,45 +2,125 @@ This file contains information about the SIMH Ethernet package. ------------------------------------------------------------------------------- -The XQ emulator is a host-independant software emulation of Digital's -DELQA (M7516) and DEQNA (M7504) Q-bus ethernet cards for the SIMH emulator. +The XQ emulator is a host-independent software emulation of Digital's +DELQA-T (M7516-YM), DELQA (M7516) and DEQNA (M7504) Q-bus Ethernet cards +for the SIMH emulator. -The XU emulator is a host-independant software emulation of Digital's DEUNA -(M7792/M7793) and DELUA (M7521) Unibus ethernet cards for the SIMH emulator. +The XU emulator is a host-independent software emulation of Digital's DEUNA +(M7792/M7793) and DELUA (M7521) Unibus Ethernet cards for the SIMH emulator. The XQ and XU simulators use the Sim_Ether module to execute host-specific -packet reads and writes, since all operating systems talk to real ethernet +packet reads and writes, since all operating systems talk to real Ethernet cards/controllers differently. See the comments at the top of sim_ether.c for the list of currently supported host platforms. -The Sim_Ether module sets the selected ethernet card into +The Sim_Ether module sets the selected Ethernet card into promiscuous mode to gather all packets, then filters out the packets that it doesn't want. In Windows, packets having the same source MAC address as the controller are ignored for WinPCAP compatibility (see Windows notes below). -If your ethernet card is plugged into a switch, the promiscuous mode setting +If your Ethernet card is plugged into a switch, the promiscuous mode setting should not cause much of a problem, since the switch will still filter out most of the undesirable traffic. You will only see "excessive" traffic if you are on a direct or hub(repeater) segment. -Using the libpcap/WinPcap interface, the simulated computer cannot "talk" to -the host computer via the selected interface, since the packets are not -reflected back to the host. The workaround for this is to use a second NIC in -the host and connect them both into the same network; then the host and the -simulator can communicate over the physical LAN. +On Windows using the WinPcap interface, the simulated computer can "talk" to +the host computer on the same interface. On other platforms with libpcap +(*nix), the simulated computer can not "talk" to the host computer via the +selected interface, since simulator transmitted packets are not received +by the host's network stack. The workaround for this is to use a second NIC +in the host and connect them both into the same network; then the host and +the simulator can communicate over the physical LAN. -Universal TUN/TAP support provides another solution for the above dual-NIC -problem for systems that support Universal TUN/TAP. Since the TUN/TAP interface -is at a different network level, the host can create a TAP device for the -simulator and then bridge or route packets between the TAP device and the real -network interface. Note that the TAP device and any bridging or routing must be -established before running the simulator; SIMH does not create, bridge, or -route TAP devices for you. +Integrated Universal TUN/TAP support provides another solution for the above +dual-NIC problem for systems that support Universal TUN/TAP. Since the TUN/TAP +interface is a pseudo network interface, the host can create a TAP device for +the simulator and then bridge or route packets between the TAP device and the +real network interface. Note that the TAP device and any bridging or routing +must be established before running the simulator; SIMH does not create, +bridge, or route TAP devices for you. + +Integrated Universal TUN/TAP support can be used for host<->simulator network +traffic (on the platforms where it is available) by using the SIMH command: +"attach xq tap:tapN" (i.e. attach xq tap:tap0). Platforms that this has been +tested on include: Linux, FreeBSD, OpenBSD, NetBSD. Each of these platforms +has some way to create a tap pseudo device and then bridge it with a physical +network interface. + +The following steps were performed to get a working SIMH vax simulator +sharing a physical NIC and allowing Host<->SIMH vax communications: + +Linux (Ubuntu 10.04): + apt-get install bridge-utils + apt-get install uml-utilities + + + #!/bin/sh + HOSTIP=`/sbin/ifconfig eth0 | grep "inet addr" | gawk -- '{ print $2 }' | gawk -F : -- '{ print $2 }'` + HOSTNETMASK=`/sbin/ifconfig eth0 | grep "inet addr" | gawk -- '{ print $4 }' | gawk -F : -- '{ print $2 }'` + HOSTBCASTADDR=`/sbin/ifconfig eth0 | grep "inet addr" | gawk -- '{ print $3 }' | gawk -F : -- '{ print $2 }'` + HOSTDEFAULTGATEWAY=`/sbin/route -n | grep ^0.0.0.0 | gawk -- '{ print $2 }'` + # + /usr/sbin/tunctl -t tap0 [-u someuser] + /sbin/ifconfig tap0 up + # + # Now convert eth0 to a bridge and bridge it with the TAP interface + /usr/sbin/brctl addbr br0 + /usr/sbin/brctl addif br0 eth0 + /usr/sbin/brctl setfd br0 0 + /sbin/ifconfig eth0 0.0.0.0 + /sbin/ifconfig br0 $HOSTIP netmask $HOSTNETMASK broadcast $HOSTBCASTADDR up + # set the default route to the br0 interface + /sbin/route add -net 0.0.0.0/0 gw $HOSTDEFAULTGATEWAY + # bridge in the tap device + /usr/sbin/brctl addif br0 tap0 + /sbin/ifconfig tap0 0.0.0.0 + + # Run simulator and "attach xq tap:tap0" + +OpenBSD (OpenBSD 4.6) + + /sbin/ifconfig tun0 create + /sbin/ifconfig tun0 link0 + /sbin/ifconfig tun0 up + + /sbin/ifconfig bridge0 create + /sbin/brconfig bridge0 fwddelay 4 + /sbin/brconfig bridge0 add em0 add tun0 # Change em0 to reflect your physical NIC name + /sbin/brconfig bridge0 up + + # Run simulator and "attach xq tap:tun0" + +FreeBSD (FreeBSD 8.0) + + /sbin/ifconfig tap0 create + /sbin/ifconfig tap0 up + + /sbin/ifconfig bridge0 create + /sbin/ifconfig bridge0 addm em0 addm tap0 # Change em0 to reflect your physical NIC name + /sbin/ifconfig bridge0 up + + # Run simulator and "attach xq tap:tap0" + # Note: it seems that on FreeBSD you may have to + # "/sbin/ifconfig tap0 up" and "/sbin/ifconfig bridge0 up" prior to each + # time simh "attach"es the tap:tap0 device + +NetBSD (NetBSD 5.0.2) + + /sbin/ifconfig tap0 create + /sbin/ifconfig tap0 up + + /sbin/ifconfig bridge0 create + /sbin/brconfig bridge0 fwddelay 1 + /sbin/brconfig bridge0 add wm0 add tap0 # Change wm0 to reflect your physical NIC name + /sbin/brconfig bridge0 up + + # Run simulator and "attach xq tap:tap0" ------------------------------------------------------------------------------- Windows notes: - 1. The Windows-specific code uses the WinPCAP 3.0 package from + 1. The Windows-specific code uses the WinPCAP 4.x package from http://www.winpcap.org. This package for windows simulates the libpcap package that is freely available for un*x systems. @@ -48,29 +128,27 @@ Windows notes: 3. The first time the WinPCAP driver is used, it will be dynamically loaded, and the user must be an Administrator on the machine to do so. If you need - to run as an unprivileged user, you must set the service to autostart. See - the WinPCAP documentation for details on the static loading workaround. - - 4. If you want to use TAP devices, they must be created before running SIMH. - (TAP component from the OpenVPN project; http://openvpn.sourceforge.net) - - 5. Compaq PATHWORKS 32 v7.2 also enabled bridging for the ethernet adapters - when the DECNET and LAT drivers were installed; TAP was not needed. + to run as an unprivileged user, you must set the "npf" driver to autostart. + Current WinPcap installers provide an option to configure this at + installation time. Building on Windows: - 1. Install WinPCAP 3.0 runtime and the WinPCAP Developer's kit. + 1. Install WinPCAP 4.x runtime and the WinPCAP Developer's kit. - 2. Put the required .h files (bittypes,devioctl,ip6_misc,packet32,pcap, - pcap-stdinc).h from the WinPCAP 3.0 developer's kit in the compiler's path + 2. Put the required .h files (bittypes,devioctl,ip6_misc,pcap,pcap-stdinc + packet32,ntddndis).h from the WinPCAP 4.x developer's kit in the + compiler's include file path - 3. Put the required .lib files (packet,wpcap).lib from the WinPCAP 3.0 - developer's kit in the linker's path + 3. Put the required .lib files (packet,wpcap).lib from the WinPCAP 4.x + developer's kit in the linker's library path 4. If you're using Borland C++, use COFF2OMF to convert the .lib files into a format that can be used by the compiler. - 5. Define USE_NETWORK. + 5. Define USE_NETWORK. The current windows network built binaries will + run on any system. regardless of whether or not WinPcap is installed, + and will provide Network functionality when WinPcap is available. 6. Build it! @@ -83,30 +161,33 @@ Linux, {Free|Net|Open}BSD, OS/X, and Un*x notes: Sim_Ether has been reworked to be more universal; because of this, you will need to get a version of libpcap that is 0.9 or greater. This can be downloaded from www.tcpdump.org - see the comments at the top of Sim_ether.c -for details. - -At the time of this release, the "Current Version" available at: -http://www.tcpdump.org/daily/libpcap-current.tar.gz is the -latest checked-in source code that is actually higher than the released -0.8.3 version number. Specifically, for all platforms, it contains code that -opens the ethernet device in Read/Write mode instead of the Read-Only mode -that previous libpcap versions for platforms which use one of pcap-bpf.c, -pcap-pf.c, or pcap-snit.c. This capabiligy now exists to support a newly -provided generic packet sending capability. +for details. ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- 1. For all platforms, you must run SIMH(scp) with sufficient privilege to - allow the ethernet card can be set into promiscuous mode and to write - packets through the driver. For most Unix/Unix-like platforms this will - mean running as root. For systems which use bpf devices (NetBSD, - OpenBSD, FreeBSD and OS/X) it is possible to set permissions on the bpf - devices to allow read and write access to users other than root (For - example: chmod 666 /dev/bpf*). Doing this, has its own security issues. + allow the Ethernet card can be set into promiscuous mode and to write + packets through the driver. + a) For Windows systems this means having administrator privileges to + start the "npf" driver. The current WinPcap installer offers an + option to autostart the "npf" driver when the system boots. + b) For more recent Linux systems, The concepts leveraging "Filesystem + Capabilities" can be used to specifically grant the simh binary + the needed privileges to access the network. The article at: + http://packetlife.net/blog/2010/mar/19/sniffing-wireshark-non-root-user/ + describes how to do this for wireshark. The exact same capabilities + are needed by SIMH for network support. Use that article as a guide. + c) For Unix/Unix-like systems which use bpf devices (NetBSD, + OpenBSD, FreeBSD and OS/X) it is possible to set permissions on + the bpf devices to allow read and write access to users other + than root (For example: chmod 666 /dev/bpf*). Doing this, has + its own security issues. + d) For other platforms this will likely mean running as root. Additional alternative methods for avoiding the 'run as root' requirement will be welcomed. - 2. If you want to use TAP devices, they must be created before running SIMH. + 2. If you want to use TAP devices, and any surrounding system network/bridge + setup must be done before running SIMH. Building on Linux, {Free|Net|Open}BSD, OS/X, Un*x: @@ -116,12 +197,18 @@ Building on Linux, {Free|Net|Open}BSD, OS/X, Un*x: Linux : search for your variant on http://rpmfind.net OS/X : Apple Developer's site? - NOTE: These repositories will not likely contain a version - of libpcap greater than 0.8.1 for several years since - other packages in these repositories don't depend on a - later version than they currently have. + NOTE: These repositories for older versions of these platforms + don't contain a version of libpcap greater than 0.8.1. + However, most(all) recent releases of *nix environments ship + with sufficiently recent versions of libpcap either automatically + installed or available for installation as part of the + distribution. - 2. Use 'make USE_NETWORK=1' + 2. If you install the vendor supplied libpcap-dev package and it provides + a /usr/lib/libpcap.a file, then the existing makefile will automatically + use the vendor supplied library without any additional arguments. + If you have downloaded and built the libpcap from tcpdump.org, then + you can use it during a build by typing 'make USE_NETWORK=1' 3. Build it! @@ -132,27 +219,27 @@ OpenVMS Alpha notes: when required VCI promiscuous mode support was added. Hobbyists can get the required version of VMS from the OpenVMS Alpha Hobbyist Kit 3.0. - Running a simulator built with ethernet support on a version of VMS prior - to 7.3-1 will behave as if there is no ethernet support built in due to + Running a simulator built with Ethernet support on a version of VMS prior + to 7.3-1 will behave as if there is no Ethernet support built in due to the inability of the software to set the PCAPVCM into promiscuous mode. - An example display of fully functional ethernet support: + An example display of fully functional Ethernet support: sim> SHOW XQ ETH ETH devices: 0 we0 (VMS Device: _EWA0:) 1 we1 (VMS Device: _EWB0:) - An example display when the simulator was built without ethernet support + An example display when the simulator was built without Ethernet support or is not running the required version of VMS: sim> SHOW XQ ETH ETH devices: no network devices are available 2. You must place the PCAPVCM.EXE execlet in SYS$LOADABLE_IMAGES before - running a simulator with ethernet support. Note: This is done by the + running a simulator with Ethernet support. Note: This is done by the build commands in descrip.mms. - 3. You must have CMKRNL privilege to SHOW or ATTACH an ethernet device; + 3. You must have CMKRNL privilege to SHOW or ATTACH an Ethernet device; alternatively, you can INSTALL the simulator with CMKRNL privilege. 4. If you use a second adapter to communicate to the host, SOME protocol @@ -162,13 +249,13 @@ OpenVMS Alpha notes: Building on OpenVMS Alpha: The current descrip.mms file will build simulators capable of using - ethernet support with them automatically. These currently are: VAX, + Ethernet support with them automatically. These currently are: VAX, PDP11, and PDP10. The descrip.mms driven builds will also build the pcap library and build and install the VCI execlet. 1. Fetch the VMS-PCAP zip file from: http://simh.trailing-edge.com/sources/vms-pcap.zip - 2. Unzip it into the base of the simh distribution directory. + 2. Unzip it into the base of the SIMH distribution directory. 3. Build the simulator(s) with MMS or MMK: $ MMx {VAX,PDP11,PDP10, etc...} @@ -195,15 +282,15 @@ RSX11M+ system image that also contains DECNET, LAT, and/or TCP/IP software. ------------------------------------------------------------------------------- -How to debug problems with the ethernet subsystems: +How to debug problems with the Ethernet subsystems: PLEASE read the host-specific notes in sim_ether.c! While running SCP, the following commands can be used to enable debug messages: - scp> SET DEBUG STDERR - scp> SET XQ DEBUG={ETH|TRC|REG|WRN|CSR|VAR|SAN|SET|PCK} - scp> SET XU DEBUG={ETH|TRC|REG|WRN} + sim> SET DEBUG STDERR + sim> SET XQ DEBUG=TRACE;CSR;VAR;WARN;SETUP;SANITY;REG;PACKET;DATA;ETH + sim> SET XU DEBUG=ETH;TRACE;REG;WARN;PACKET;DATA Documentation of the functionality of these debug modifiers can be found in pdp11_xq.h and pdp11_xu.h. Inline debugging has replaced the previous #ifdef @@ -212,16 +299,13 @@ style of debugging, which required recompilation before debugging. ------------------------------------------------------------------------------- Things planned for future releases: - 1. PDP-11 bootstrap/bootrom - 2. Full MOP implementation - 3. DESQA support (if someone can get me the user manuals) - 4. DETQA support [DELQA-Turbo] (I have the manual) + 1. Full MOP implementation ------------------------------------------------------------------------------- Things which I need help with: 1. Information about Remote MOP processing - 2. VAX/PDP-11 hardware diagnotics image files and docs, to test XQ thoroughly. + 2. VAX/PDP-11 hardware diagnostics image files and docs, to test XQ thoroughly. 3. Feedback on operation with other VAX/PDP-11 OS's. ------------------------------------------------------------------------------- @@ -237,11 +321,160 @@ Dave Change Log =============================================================================== + 12-Jan-11 DTH Added SHOW XU FILTERS modifier + 11-Jan-11 DTH Corrected DEUNA/DELUA SELFTEST command, enabling use by + VMS 3.7, VMS 4.7, and Ultrix 1.1 + 09-Jan-11 MP Fixed missing crc data when USE_READER_THREAD is defined and + crc's are needed (only the pdp11_xu) + 16-Dec-10 MP added priority boost for read and write threads when + USE_READER_THREAD does I/O in separate threads. This helps + throughput since it allows these I/O bound threads to preempt + the main thread (which is executing simulated instructions). + 09-Dec-10 MP allowed more flexible parsing of MAC address strings + 09-Dec-10 MP Added support to determine if network address conflicts exist + 07-Dec-10 MP Reworked DECnet self detection to the more general approach + of loopback self when any Physical Address is being set. + 06-Dec-10 MP Added loopback processing support to pdp11_xu.c + 06-Dec-10 MP Fixed loopback processing to correctly handle forward packets. + 04-Dec-10 MP Changed eth_write to do nonblocking writes when + USE_READER_THREAD is defined. + 30-Nov-10 MP Fixed the fact that no broadcast packets were received by the DEUNA + 29-Nov-10 MP Fixed interrupt dispatch issue which caused delivered packets + (in and out) to sometimes not interrupt the CPU after processing. + 17-Jun-10 MP Fixed bug in the AUTODIN II hash filtering. + 14-Jun-10 MP Added support for integrated Tap networking interfaces on BSD + platforms. + 13-Jun-10 MP Added support for integrated Tap networking interfaces on Linux + platforms. + 31-May-10 MP Added support for more TOE (TCP Offload Engine) features for IPv4 + network traffic from the host and/or from hosts on the LAN. These + new TOE features are: LSO (Large Send Offload) and Jumbo packet + fragmentation support. These features allow a simulated network + device to support traffic when a host leverages a NIC's Large + Send Offload capabilities to fragment and/or segment outgoing + network traffic. Additionally a simulated network device can + reasonably exist on a LAN which is configured to use Jumbo frames. + 21-May-10 MP Added functionality to fix up IP header checksums to accommodate + packets from a host with a NIC which has TOE (TCP Offload Engine) + enabled which is expected to implement the checksum computations + in hardware. Since we catch packets before they arrive at the + NIC the expected checksum insertions haven't been performed yet. + This processing is only done for packets sent from the host to + the guest we're supporting. In general this will be a relatively + small number of packets so it is done for all IP frame packets + coming from the host to the guest. In order to make the + determination of packets specifically arriving from the host we + need to know the hardware MAC address of the host NIC. Currently + determining a NIC's MAC address is relatively easy on Windows. + The non-windows code works on linux and may work on other *nix + platforms either as is or with slight modifications. The code, + as implemented, only messes with this activity if the host + interface MAC address can be determined. + 20-May-10 MP Added general support to deal with receiving packets smaller + than ETH_MIN_PACKET in length. These come from packets + looped back by some bridging mechanism and need to be padded + to the minimum frame size. A real NIC won't pass us any + packets like that. This fix belongs here since this layer + is responsible for interfacing to the physical layer + devices, AND it belongs here to get CRC processing right. + 15-Aug-08 MP Fixed transmitted packets to have the correct source MAC address. + Fixed incorrect address filter setting calling eth_filter(). + 07-Mar-08 MP Fixed the SCP visible SA registers to always display the + ROM MAC address, even after it is changed by SET XQ MAC=. + 07-Mar-08 MP Added changes so that the Console DELQA diagnostic (>>>TEST 82) + will succeed. + 03-Mar-08 MP Added DELQA-T (aka DELQA Plus) device emulation support. + 06-Feb-08 MP Added dropped frame statistics to record when the receiver discards + received packets due to the receiver being disabled, or due to the + XQ device's packet receive queue being full. + Fixed bug in receive processing when we're not polling. This could + cause receive processing to never be activated again if we don't + read all available packets via eth_read each time we get the + opportunity. + 31-Jan-08 MP Added the ability to Coalesce received packet interrupts. This + is enabled by SET XQ POLL=DELAY=nnn where nnn is a number of + microseconds to delay the triggering of an interrupt when a packet + is received. + 29-Jan-08 MP Added SET XQ POLL=DISABLE (aka SET XQ POLL=0) to operate without + polling for packet read completion. + 29-Jan-08 MP Changed the sanity and id timer mechanisms to use a separate timer + unit so that transmit and receive activities can be dealt with + by the normal xq_svc routine. + Dynamically determine the timer polling rate based on the + calibrated tmr_poll and clk_tps values of the simulator. + 25-Jan-08 MP Enabled the SET XQ POLL to be meaningful if the simulator currently + doesn't support idling. + 25-Jan-08 MP Changed xq_debug_setup to use sim_debug instead of printf so that + all debug output goes to the same place. + 25-Jan-08 MP Restored the call to xq_svc after all successful calls to eth_write + to allow receive processing to happen before the next event + service time. This must have been inadvertently commented out + while other things were being tested. + 23-Jan-08 MP Added debugging support to display packet headers and packet data + 18-Jun-07 RMS Added UNIT_IDLE flag + 29-Oct-06 RMS Synced poll and clock + 27-Jan-06 RMS Fixed unaligned accesses in XQB (found by Doug Carman) + 07-Jan-06 RMS Fixed unaligned access bugs (found by Doug Carman) + 07-Sep-05 DTH Removed unused variable + 16-Aug-05 RMS Fixed C++ declaration and cast problems + + 05-Mar-08 MP Added optional multicast filtering support for doing + LANCE style AUTODIN II based hashed filtering. + 07-Feb-08 MP Added eth_show_dev to display Ethernet state + Changed the return value from eth_read to return whether + or not a packet was read. No existing callers used or + checked constant return value that previously was being + supplied. + 29-Jan-08 MP Added eth_set_async to provide a mechanism (when + USE_READER_THREAD is enabled) to allow packet reception + to dynamically update the simulator event queue and + potentially avoid polling for I/O. This provides a minimal + overhead (no polling) maximal responsiveness for network + activities. + 29-Jan-08 MP Properly sequenced activities in eth_close to avoid a race + condition when USE_READER_THREAD is enabled. + 25-Jan-08 MP Changed the following when USE_READER_THREAD is enabled: + - Fixed bug when the simulated device doesn't need crc + in packet data which is read. + - Added call to pcap_setmintocopy to minimize packet + delivery latencies. + - Added ethq_destroy and used it to avoid a memory leak in + eth_close. + - Properly cleaned up pthread mutexes in eth_close. + Migrated to using sim_os_ms_sleep for a delay instead of + a call to select(). + Fixed the bpf filter used when no traffic is to be matched. + Reworked eth_add_packet_crc32 implementation to avoid an + extra buffer copy while reading packets. + Fixed up #ifdef's relating to USE_SHARED so that setting + USE_SHARED or USE_NETWORK will build a working network + environment. + 23-Jan-08 MP Reworked eth_packet_trace and eth_packet_trace_ex to allow + only output Ethernet header+crc and provide a mechanism for + the simulated device to display full packet data debugging. + 17-May-07 DTH Fixed non-Ethernet device removal loop (from Naoki Hamada) + 15-May-07 DTH Added dynamic loading of wpcap.dll; + Corrected exceed max index bug in ethX lookup + 04-May-07 DTH Corrected failure to look up Ethernet device names in + the registry on Windows XP x64 + 10-Jul-06 RMS Fixed linux conditionalization (from Chaskiel Grundman) + 02-Jun-06 JDB Fixed compiler warning for incompatible sscanf parameter + 15-Dec-05 DTH Patched eth_host_devices [remove non-Ethernet devices] + (from Mark Pizzolato and Galen Tackett, 08-Jun-05) + Patched eth_open [tun fix](from Antal Ritter, 06-Oct-05) + 30-Nov-05 DTH Added option to regenerate CRC on received packets; some + Ethernet devices need to pass it on to the simulation, and by + the time libpcap/winpcap gets the packet, the host OS network + layer has already stripped CRC out of the packet + 01-Dec-04 DTH Added Windows user-defined adapter names (from Timothe Litt) + + + 19-Mar-04 Release: 1. Genericized Sim_Ether code, reduced #ifdefs (David Hittner) 2. Further refinement of sim_ether, qualified more platforms (Mark Pizzolato) 3. Added XU module (David Hittner) - 4. Corrected XQ interrupt signalling for PDP11s (David Hittner) + 4. Corrected XQ interrupt signaling for PDP11s (David Hittner) 5. Added inline debugging support (David Hittner) ------------------------------------------------------------------------------- @@ -251,7 +484,7 @@ Dave 2. Added DECNET duplicate detection for Windows (Mark Pizzolato) 3. Added BPF filtering to increase efficiency (Mark Pizzolato) 4. Corrected XQ Runt processing (Mark Pizzolato) - 5. Corrected XQ Sofware Reset (Mark Pizzolato) + 5. Corrected XQ Software Reset (Mark Pizzolato) 6. Corrected XQ Multicast/Promiscuous mode setting/resetting (Mark Pizzolato) 7. Added Universal TUN/TAP support (Mark Pizzolato) 8. Added FreeBSD support (Edward Brocklesby) @@ -271,7 +504,7 @@ Dave 1. Corrected bug in xq_setmac introduced in v3.0 (multiple people) 2. Made XQ rcv buffer allocation dynamic to reduce scp size (David Hittner) 3. Optimized some structs, removed legacy variables (Mark Pizzolato) - 4. Changed #ifdef WIN32 to _WIN32 for consistancy (Mark Pizzolato) + 4. Changed #ifdef WIN32 to _WIN32 for consistency (Mark Pizzolato) ------------------------------------------------------------------------------- @@ -308,7 +541,7 @@ Dave 08-Nov-02 Release: 1. Added USE_NETWORK conditional to Sim_Ether - 2. Fixed behaviour of SHOW XQ ETH if no devices exist + 2. Fixed behavior of SHOW XQ ETH if no devices exist 3. Added OpenBSD support to Sim_Ether (courtesy of Federico Schwindt) 4. Added ethX detection simplification (from Megan Gentry) diff --git a/PDP11/pdp11_xq.c b/PDP11/pdp11_xq.c index 06ef780f..89591bad 100644 --- a/PDP11/pdp11_xq.c +++ b/PDP11/pdp11_xq.c @@ -26,11 +26,12 @@ ------------------------------------------------------------------------------ - This DEQNA/DELQA simulation is based on: + This DEQNA/DELQA/DELQA-T simulation is based on: Digital DELQA Users Guide, Part# EK-DELQA-UG-002 Digital DEQNA Users Guide, Part# EK-DEQNA-UG-001 + Digital DELQA-Plus Addendum to DELQA Users Guide, Part# EK-DELQP-UG-001_Sep89.pdf These manuals can be found online at: - http://www.spies.com/~aek/pdf/dec/qbus + http://www.bitsavers.org/pdf/dec/qbus Certain adaptations have been made because this is an emulation: Ethernet transceiver power flag CSR<12> is ON when attached. @@ -58,6 +59,7 @@ 5. VMS LAT - SET HOST/LAT tests 6. VMS Cluster - SHOW CLUSTER, SHOW DEVICE, and cluster COPY tests 7. Console boot into VMSCluster (>>>B XQAO) + 8. Console DELQA Diagnostic (>>>TEST 82) PDP11: 1. RT-11 v5.3 - FTPSB copy test 2. RSTS/E v10.1 - detects/enables device @@ -66,6 +68,42 @@ Modification history: + 09-Dec-10 MP Added address conflict check during attach. + 06-Dec-10 MP Fixed loopback processing to correctly handle forward packets. + 29-Nov-10 MP Fixed interrupt dispatch issue which caused delivered packets + (in and out) to sometimes not interrupt the CPU after processing. + 07-Mar-08 MP Fixed the SCP visibile SA registers to always display the + ROM mac address, even after it is changed by SET XQ MAC=. + 07-Mar-08 MP Added changes so that the Console DELQA diagnostic (>>>TEST 82) + will succeed. + 03-Mar-08 MP Added DELQA-T (aka DELQA Plus) device emulation support. + 06-Feb-08 MP Added dropped frame statistics to record when the receiver discards + received packets due to the receiver being disabled, or due to the + XQ device's packet receive queue being full. + Fixed bug in receive processing when we're not polling. This could + cause receive processing to never be activated again if we don't + read all available packets via eth_read each time we get the + opportunity. + 31-Jan-08 MP Added the ability to Coalesce received packet interrupts. This + is enabled by SET XQ POLL=DELAY=nnn where nnn is a number of + microseconds to delay the triggering of an interrupt when a packet + is received. + 29-Jan-08 MP Added SET XQ POLL=DISABLE (aka SET XQ POLL=0) to operate without + polling for packet read completion. + 29-Jan-08 MP Changed the sanity and id timer mechanisms to use a separate timer + unit so that transmit and recieve activities can be dealt with + by the normal xq_svc routine. + Dynamically determine the timer polling rate based on the + calibrated tmr_poll and clk_tps values of the simulator. + 25-Jan-08 MP Enabled the SET XQ POLL to be meaningful if the simulator currently + doesn't support idling. + 25-Jan-08 MP Changed xq_debug_setup to use sim_debug instead of printf so that + all debug output goes to the same place. + 25-Jan-08 MP Restored the call to xq_svc after all successful calls to eth_write + to allow receive processing to happen before the next event + service time. This must have been inadvertently commented out + while other things were being tested. + 23-Jan-08 MP Added debugging support to display packet headers and packet data 18-Jun-07 RMS Added UNIT_IDLE flag 29-Oct-06 RMS Synced poll and clock 27-Jan-06 RMS Fixed unaligned accesses in XQB (found by Doug Carman) @@ -90,7 +128,7 @@ device in promiscuous or all multicast mode once it ever had been there. - Fixed output format in show_xq_sanity to end in "\n" - - Added display of All Multicase and promiscuous to + - Added display of All Multicast and promiscuous to xq_show_filters - The stuck in All Multicast or Promiscuous issue is worse than previously thought. See comments in @@ -99,7 +137,7 @@ separator character, since sim_ether's eth_mac_fmt formats them with this separator character. - Changed xq_sw_reset to behave more like the set of - actions described in Table 3-6 of the DELQA manua. + actions described in Table 3-6 of the DELQA manual. The manual mentions "N/A" which I'm interpreting to mean "Not Affected". 05-Jun-03 DTH Added receive packet splitting @@ -212,13 +250,17 @@ #include "pdp11_xq_bootrom.h" extern int32 tmxr_poll; +extern int32 tmr_poll, clk_tps; +extern t_bool sim_idle_enab; extern FILE* sim_deb; +extern FILE *sim_log; extern char* read_line (char *ptr, int32 size, FILE *stream); /* forward declarations */ t_stat xq_rd(int32* data, int32 PA, int32 access); t_stat xq_wr(int32 data, int32 PA, int32 access); t_stat xq_svc(UNIT * uptr); +t_stat xq_tmrsvc(UNIT * uptr); t_stat xq_reset (DEVICE * dptr); t_stat xq_attach (UNIT * uptr, char * cptr); t_stat xq_detach (UNIT * uptr); @@ -235,7 +277,10 @@ t_stat xq_show_poll (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xq_set_poll (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat xq_process_xbdl(CTLR* xq); t_stat xq_dispatch_xbdl(CTLR* xq); -void xq_start_receiver(void); +t_stat xq_process_turbo_rbdl(CTLR* xq); +t_stat xq_process_turbo_xbdl(CTLR* xq); +void xq_start_receiver(CTLR* xq); +void xq_stop_receiver(CTLR* xq); void xq_sw_reset(CTLR* xq); t_stat xq_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat xq_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); @@ -255,8 +300,10 @@ struct xq_device xqa = { xqa_read_callback, /* read callback routine */ xqa_write_callback, /* write callback routine */ {0x08, 0x00, 0x2B, 0xAA, 0xBB, 0xCC}, /* mac */ - XQ_T_DELQA, /* type */ + XQ_T_DELQA_PLUS, /* type */ + XQ_T_DELQA, /* mode */ XQ_SERVICE_INTERVAL, /* poll */ + 0, 0, /* coalesce */ {0} /* sanity */ }; @@ -264,8 +311,10 @@ struct xq_device xqb = { xqb_read_callback, /* read callback routine */ xqb_write_callback, /* write callback routine */ {0x08, 0x00, 0x2B, 0xBB, 0xCC, 0xDD}, /* mac */ - XQ_T_DELQA, /* type */ + XQ_T_DELQA_PLUS, /* type */ + XQ_T_DELQA, /* mode */ XQ_SERVICE_INTERVAL, /* poll */ + 0, 0, /* coalesce */ {0} /* sanity */ }; @@ -275,15 +324,18 @@ DIB xqa_dib = { IOBA_XQ, IOLN_XQ, &xq_rd, &xq_wr, UNIT xqa_unit[] = { { UDATA (&xq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 2047) }, /* receive timer */ + { UDATA (&xq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0) }, }; REG xqa_reg[] = { - { GRDATA ( SA0, xqa.addr[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA1, xqa.addr[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA2, xqa.addr[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA3, xqa.addr[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA4, xqa.addr[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA5, xqa.addr[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA0, xqa.mac[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA1, xqa.mac[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA2, xqa.mac[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA3, xqa.mac[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA4, xqa.mac[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA5, xqa.mac[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( MX0, xqa.mac_checksum[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( MX1, xqa.mac_checksum[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( RBDL, xqa.rbdl[0], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( RBDH, xqa.rbdl[1], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( XBDL, xqa.xbdl[0], XQ_RDX, 16, 0), REG_FIT }, @@ -291,6 +343,13 @@ REG xqa_reg[] = { { GRDATA ( VAR, xqa.var, XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( CSR, xqa.csr, XQ_RDX, 16, 0), REG_FIT }, { FLDATA ( INT, xqa.irq, 0) }, + { GRDATA ( TYPE, xqa.type, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA ( MODE, xqa.mode, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA ( POLL, xqa.poll, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA ( CLAT, xqa.coalesce_latency, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA ( CLATT, xqa.coalesce_latency_ticks, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA ( RBDL_BA, xqa.rbdl_ba, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( XBDL_BA, xqa.xbdl_ba, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_PRM, xqa.setup.promiscuous, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_MLT, xqa.setup.multicast, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_L1, xqa.setup.l1, XQ_RDX, 32, 0), REG_HRO}, @@ -298,6 +357,17 @@ REG xqa_reg[] = { { GRDATA ( SETUP_L3, xqa.setup.l3, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_SAN, xqa.setup.sanity_timer, XQ_RDX, 32, 0), REG_HRO}, { BRDATA ( SETUP_MACS, &xqa.setup.macs, XQ_RDX, 8, sizeof(xqa.setup.macs)), REG_HRO}, + { BRDATA ( STATS, &xqa.stats, XQ_RDX, 8, sizeof(xqa.setup.macs)), REG_HRO}, + { BRDATA ( TURBO_INIT, &xqa.init, XQ_RDX, 8, sizeof(xqa.init)), REG_HRO}, + { GRDATA ( SRR, xqa.srr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( SRQR, xqa.srqr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( IBA, xqa.iba, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA ( ICR, xqa.icr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( IPEND, xqa.pending_interrupt, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( TBINDX, xqa.tbindx, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( RBINDX, xqa.rbindx, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( IDTMR, xqa.idtmr, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( MUST_POLL, xqa.must_poll, XQ_RDX, 32, 0), REG_HRO}, { NULL }, }; @@ -306,15 +376,18 @@ DIB xqb_dib = { IOBA_XQB, IOLN_XQB, &xq_rd, &xq_wr, UNIT xqb_unit[] = { { UDATA (&xq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 2047) }, /* receive timer */ + { UDATA (&xq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0) }, }; REG xqb_reg[] = { - { GRDATA ( SA0, xqb.addr[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA1, xqb.addr[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA2, xqb.addr[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA3, xqb.addr[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA4, xqb.addr[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA5, xqb.addr[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA0, xqb.mac[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA1, xqb.mac[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA2, xqb.mac[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA3, xqb.mac[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA4, xqb.mac[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA5, xqb.mac[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( MX0, xqb.mac_checksum[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( MX1, xqb.mac_checksum[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( RBDL, xqb.rbdl[0], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( RBDH, xqb.rbdl[1], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( XBDL, xqb.xbdl[0], XQ_RDX, 16, 0), REG_FIT }, @@ -322,6 +395,13 @@ REG xqb_reg[] = { { GRDATA ( VAR, xqb.var, XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( CSR, xqb.csr, XQ_RDX, 16, 0), REG_FIT }, { FLDATA ( INT, xqb.irq, 0) }, + { GRDATA ( TYPE, xqb.type, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA ( MODE, xqb.mode, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA ( POLL, xqb.poll, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA ( CLAT, xqb.coalesce_latency, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA ( CLATT, xqb.coalesce_latency_ticks, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA ( RBDL_BA, xqb.rbdl_ba, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( XBDL_BA, xqb.xbdl_ba, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_PRM, xqb.setup.promiscuous, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_MLT, xqb.setup.multicast, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_L1, xqb.setup.l1, XQ_RDX, 32, 0), REG_HRO}, @@ -329,6 +409,17 @@ REG xqb_reg[] = { { GRDATA ( SETUP_L3, xqb.setup.l3, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_SAN, xqb.setup.sanity_timer, XQ_RDX, 32, 0), REG_HRO}, { BRDATA ( SETUP_MACS, &xqb.setup.macs, XQ_RDX, 8, sizeof(xqb.setup.macs)), REG_HRO}, + { BRDATA ( STATS, &xqb.stats, XQ_RDX, 8, sizeof(xqa.setup.macs)), REG_HRO}, + { BRDATA ( TURBO_INIT, &xqb.init, XQ_RDX, 8, sizeof(xqb.init)), REG_HRO}, + { GRDATA ( SRR, xqb.srr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( SRQR, xqb.srqr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( IBA, xqb.iba, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA ( ICR, xqb.icr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( IPEND, xqb.pending_interrupt, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( TBINDX, xqb.tbindx, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( RBINDX, xqb.rbindx, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( IDTMR, xqb.idtmr, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( MUST_POLL, xqb.must_poll, XQ_RDX, 32, 0), REG_HRO}, { NULL }, }; @@ -345,10 +436,15 @@ MTAB xq_mod[] = { NULL, &xq_show_filters, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATS", "STATS", &xq_set_stats, &xq_show_stats, NULL }, - { MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE={DEQNA|DELQA}", + { MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE={DEQNA|DELQA|DELQA-T}", &xq_set_type, &xq_show_type, NULL }, - { MTAB_XTD | MTAB_VDV, 0, "POLL", "POLL={DEFAULT|4..2500]", +#ifdef USE_READER_THREAD + { MTAB_XTD | MTAB_VDV, 0, "POLL", "POLL={DEFAULT|DISABLED|4..2500|DELAY=nnn}", &xq_set_poll, &xq_show_poll, NULL }, +#else + { MTAB_XTD | MTAB_VDV, 0, "POLL", "POLL={DEFAULT|DISABLED|4..2500}", + &xq_set_poll, &xq_show_poll, NULL }, +#endif { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "SANITY", "SANITY={ON|OFF}", &xq_set_sanity, &xq_show_sanity, NULL }, { 0 }, @@ -363,13 +459,14 @@ DEBTAB xq_debug[] = { {"SANITY", DBG_SAN}, {"REG", DBG_REG}, {"PACKET", DBG_PCK}, + {"DATA", DBG_DAT}, {"ETH", DBG_ETH}, {0} }; DEVICE xq_dev = { "XQ", xqa_unit, xqa_reg, xq_mod, - 1, XQ_RDX, 11, 1, XQ_RDX, 16, + 2, XQ_RDX, 11, 1, XQ_RDX, 16, &xq_ex, &xq_dep, &xq_reset, NULL, &xq_attach, &xq_detach, &xqa_dib, DEV_DISABLE | DEV_QBUS | DEV_DEBUG, @@ -378,7 +475,7 @@ DEVICE xq_dev = { DEVICE xqb_dev = { "XQB", xqb_unit, xqb_reg, xq_mod, - 1, XQ_RDX, 11, 1, XQ_RDX, 16, + 2, XQ_RDX, 11, 1, XQ_RDX, 16, &xq_ex, &xq_dep, &xq_reset, NULL, &xq_attach, &xq_detach, &xqb_dib, DEV_DISABLE | DEV_DIS | DEV_QBUS | DEV_DEBUG, @@ -394,8 +491,16 @@ const char* const xq_recv_regnames[] = { "MAC0", "MAC1", "MAC2", "MAC3", "MAC4", "MAC5", "VAR", "CSR" }; +const char* const xqt_recv_regnames[] = { + "MAC0", "MAC1", "MAC2", "MAC3", "MAC4", "MAC5", "SRR", "" +}; + const char* const xq_xmit_regnames[] = { - "", "", "RBDL-Lo", "RBDL-Hi", "XBDL-Lo", "XBDL-Hi", "VAR", "CSR" + "XCR0", "XCR1", "RBDL-Lo", "RBDL-Hi", "XBDL-Lo", "XBDL-Hi", "VAR", "CSR" +}; + +const char* const xqt_xmit_regnames[] = { + "IBAL", "IBAH", "ICR", "", "SRQR", "", "", "ARQR" }; const char* const xq_csr_bits[] = { @@ -408,8 +513,14 @@ const char* const xq_var_bits[] = { "V6", "V7", "S1", "S2", "S3", "RS", "OS", "MS" }; +const char* const xq_srr_bits[] = { + "RS0", "RS1", "", "", "", "", "", "", + "", "TBL", "IME", "PAR", "NXM", "", "CHN", "FES" +}; + /* internal debugging routines */ void xq_debug_setup(CTLR* xq); +void xq_debug_turbo_setup(CTLR* xq); /*============================================================================*/ @@ -543,16 +654,19 @@ t_stat xq_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc) char* fmt = " %-15s%d\n"; CTLR* xq = xq_unit2ctlr(uptr); - fprintf(st, "Ethernet statistics:\n"); - fprintf(st, fmt, "Recv:", xq->var->stats.recv); - fprintf(st, fmt, "Filtered:", xq->var->stats.filter); - fprintf(st, fmt, "Xmit:", xq->var->stats.xmit); - fprintf(st, fmt, "Xmit Fail:", xq->var->stats.fail); - fprintf(st, fmt, "Runts:", xq->var->stats.runt); - fprintf(st, fmt, "Oversize:", xq->var->stats.giant); - fprintf(st, fmt, "Setup:", xq->var->stats.setup); - fprintf(st, fmt, "Loopback:", xq->var->stats.loop); - fprintf(st, fmt, "ReadQ high:", xq->var->ReadQ.high); + fprintf(st, "XQ Ethernet statistics:\n"); + fprintf(st, fmt, "Recv:", xq->var->stats.recv); + fprintf(st, fmt, "Dropped:", xq->var->stats.dropped + xq->var->ReadQ.loss); + fprintf(st, fmt, "Xmit:", xq->var->stats.xmit); + fprintf(st, fmt, "Xmit Fail:", xq->var->stats.fail); + fprintf(st, fmt, "Runts:", xq->var->stats.runt); + fprintf(st, fmt, "Oversize:", xq->var->stats.giant); + fprintf(st, fmt, "SW Reset:", xq->var->stats.reset); + fprintf(st, fmt, "Setup:", xq->var->stats.setup); + fprintf(st, fmt, "Loopback:", xq->var->stats.loop); + fprintf(st, fmt, "ReadQ count:", xq->var->ReadQ.count); + fprintf(st, fmt, "ReadQ high:", xq->var->ReadQ.high); + eth_show_dev(st, xq->var->etherface); return SCPE_OK; } @@ -562,15 +676,28 @@ t_stat xq_show_filters (FILE* st, UNIT* uptr, int32 val, void* desc) char buffer[20]; int i; - fprintf(st, "Filters:\n"); - for (i=0; ivar->setup.macs[i], buffer); - fprintf(st, " [%2d]: %s\n", i, buffer); - }; - if (xq->var->setup.multicast) - fprintf(st, "All Multicast Receive Mode\n"); - if (xq->var->setup.promiscuous) - fprintf(st, "Promiscuous Receive Mode\n"); + if (xq->var->mode == XQ_T_DELQA_PLUS) { + eth_mac_fmt(&xq->var->init.phys, buffer); + fprintf(st, "Physical Address=%s\n", buffer); + if (xq->var->etherface->hash_filter) { + fprintf(st, "Multicast Hash: "); + for (i=0; ivar->etherface->hash); ++i) + fprintf(st, "%02X ", xq->var->etherface->hash[i]); + fprintf(st, "\n"); + } + if (xq->var->init.mode & XQ_IN_MO_PRO) + fprintf(st, "Promiscuous Receive Mode\n"); + } else { + fprintf(st, "Filters:\n"); + for (i=0; ivar->setup.macs[i], buffer); + fprintf(st, " [%2d]: %s\n", i, buffer); + } + if (xq->var->setup.multicast) + fprintf(st, "All Multicast Receive Mode\n"); + if (xq->var->setup.promiscuous) + fprintf(st, "Promiscuous Receive Mode\n"); + } return SCPE_OK; } @@ -581,6 +708,15 @@ t_stat xq_show_type (FILE* st, UNIT* uptr, int32 val, void* desc) switch (xq->var->type) { case XQ_T_DEQNA: fprintf(st, "DEQNA"); break; case XQ_T_DELQA: fprintf(st, "DELQA"); break; + case XQ_T_DELQA_PLUS: fprintf(st, "DELQA-T"); break; + } + if (xq->var->type != xq->var->mode) { + fprintf(st, ",mode="); + switch (xq->var->mode) { + case XQ_T_DEQNA: fprintf(st, "DEQNA"); break; + case XQ_T_DELQA: fprintf(st, "DELQA"); break; + case XQ_T_DELQA_PLUS: fprintf(st, "DELQA-T"); break; + } } return SCPE_OK; } @@ -594,7 +730,11 @@ t_stat xq_set_type (UNIT* uptr, int32 val, char* cptr, void* desc) /* this assumes that the parameter has already been upcased */ if (!strcmp(cptr, "DEQNA")) xq->var->type = XQ_T_DEQNA; else if (!strcmp(cptr, "DELQA")) xq->var->type = XQ_T_DELQA; + else if (!strcmp(cptr, "DELQA-T")) xq->var->type = XQ_T_DELQA_PLUS; else return SCPE_ARG; + xq->var->mode = XQ_T_DELQA; + if (xq->var->type == XQ_T_DEQNA) + xq->var->mode = XQ_T_DEQNA; return SCPE_OK; } @@ -602,7 +742,13 @@ t_stat xq_set_type (UNIT* uptr, int32 val, char* cptr, void* desc) t_stat xq_show_poll (FILE* st, UNIT* uptr, int32 val, void* desc) { CTLR* xq = xq_unit2ctlr(uptr); - fprintf(st, "poll=%d", xq->var->poll); + if (xq->var->poll) + fprintf(st, "poll=%d", xq->var->poll); + else { + fprintf(st, "polling=disabled"); + if (xq->var->coalesce_latency) + fprintf(st, ",latency=%d", xq->var->coalesce_latency); + } return SCPE_OK; } @@ -615,10 +761,22 @@ t_stat xq_set_poll (UNIT* uptr, int32 val, char* cptr, void* desc) /* this assumes that the parameter has already been upcased */ if (!strcmp(cptr, "DEFAULT")) xq->var->poll = XQ_SERVICE_INTERVAL; + else if ((!strcmp(cptr, "DISABLED")) || (!strncmp(cptr, "DELAY=", 6))) { + xq->var->poll = 0; + if (!strncmp(cptr, "DELAY=", 6)) { + int delay = 0; + if (1 != sscanf(cptr+6, "%d", &delay)) + return SCPE_ARG; + xq->var->coalesce_latency = delay; + xq->var->coalesce_latency_ticks = (tmr_poll * clk_tps * xq->var->coalesce_latency) / 1000000; + } + } else { int newpoll = 0; - sscanf(cptr, "%d", &newpoll); - if ((newpoll >= 4) && (newpoll <= 2500)) + if (1 != sscanf(cptr, "%d", &newpoll)) + return SCPE_ARG; + if ((newpoll == 0) || + ((!sim_idle_enab) && (newpoll >= 4) && (newpoll <= 2500))) xq->var->poll = newpoll; else return SCPE_ARG; @@ -660,8 +818,13 @@ t_stat xq_nxm_error(CTLR* xq) const uint16 set_bits = XQ_CSR_NI | XQ_CSR_XI | XQ_CSR_XL | XQ_CSR_RL; sim_debug(DBG_WRN, xq->dev, "Non Existent Memory Error!\n"); - /* set NXM and associated bits in CSR */ - xq_csr_set_clr(xq, set_bits , 0); + if (xq->var->mode == XQ_T_DELQA_PLUS) { + /* set NXM and associated bits in SRR */ + xq->var->srr |= (XQ_SRR_FES | XQ_SRR_NXM); + xq_setint(xq); + } else + /* set NXM and associated bits in CSR */ + xq_csr_set_clr(xq, set_bits , 0); return SCPE_OK; } @@ -670,7 +833,6 @@ t_stat xq_nxm_error(CTLR* xq) */ void xq_write_callback (CTLR* xq, int status) { - t_stat rstatus; int32 wstatus; const uint16 TDR = 100 + xq->var->write_buffer.len * 8; /* arbitrary value */ uint16 write_success[2] = {0}; @@ -681,6 +843,8 @@ void xq_write_callback (CTLR* xq, int status) xq->var->stats.xmit += 1; /* update write status words */ if (status == 0) { /* success */ + if (DBG_PCK & xq->dev->dctrl) + eth_packet_trace_ex(xq->var->etherface, xq->var->write_buffer.msg, xq->var->write_buffer.len, "xq-write", DBG_DAT & xq->dev->dctrl, DBG_PCK); wstatus = Map_WriteW(xq->var->xbdl_ba + 8, 4, write_success); } else { /* failure */ sim_debug(DBG_WRN, xq->dev, "Packet Write Error!\n"); @@ -701,11 +865,6 @@ void xq_write_callback (CTLR* xq, int status) /* clear write buffer */ xq->var->write_buffer.len = 0; - /* next descriptor (implicit) */ - xq->var->xbdl_ba += 12; - - /* finish processing xbdl */ - rstatus = xq_process_xbdl(xq); } void xqa_write_callback (int status) @@ -724,7 +883,7 @@ t_stat xq_rd(int32* data, int32 PA, int32 access) CTLR* xq = xq_pa2ctlr(PA); int index = (PA >> 1) & 07; /* word index */ - sim_debug(DBG_REG, xq->dev, "xq_rd(PA=0x%08X [%s], access=%d)\n", PA, xq_recv_regnames[index], access); + sim_debug(DBG_REG, xq->dev, "xq_rd(PA=0x%08X [%s], access=%d)\n", PA, ((xq->var->mode == XQ_T_DELQA_PLUS) ? xqt_recv_regnames[index] : xq_recv_regnames[index]), access); switch (index) { case 0: case 1: @@ -741,9 +900,14 @@ t_stat xq_rd(int32* data, int32 PA, int32 access) *data = 0xFF00 | xq->var->mac[index]; break; case 6: - sim_debug_u16(DBG_VAR, xq->dev, xq_var_bits, xq->var->var, xq->var->var, 0); - sim_debug (DBG_VAR, xq->dev, ", vec = 0%o\n", (xq->var->var & XQ_VEC_IV)); - *data = xq->var->var; + if (xq->var->mode != XQ_T_DELQA_PLUS) { + sim_debug_u16(DBG_VAR, xq->dev, xq_var_bits, xq->var->var, xq->var->var, 0); + sim_debug (DBG_VAR, xq->dev, ", vec = 0%o\n", (xq->var->var & XQ_VEC_IV)); + *data = xq->var->var; + } else { + sim_debug_u16(DBG_VAR, xq->dev, xq_srr_bits, xq->var->srr, xq->var->srr, 0); + *data = xq->var->srr; + } break; case 7: sim_debug_u16(DBG_CSR, xq->dev, xq_csr_bits, xq->var->csr, xq->var->csr, 1); @@ -765,6 +929,9 @@ t_stat xq_process_rbdl(CTLR* xq) ETH_ITEM* item; uint8* rbuf; + if (xq->var->mode == XQ_T_DELQA_PLUS) + return xq_process_turbo_rbdl(xq); + sim_debug(DBG_TRC, xq->dev, "xq_process_rdbl\n"); /* process buffer descriptors */ @@ -868,6 +1035,7 @@ t_stat xq_process_rbdl(CTLR* xq) if (xq->var->ReadQ.loss) { sim_debug(DBG_WRN, xq->dev, "ReadQ overflow!\n"); xq->var->rbdl_buf[4] |= 0x0001; /* set overflow bit */ + xq->var->stats.dropped += xq->var->ReadQ.loss; xq->var->ReadQ.loss = 0; /* reset loss counter */ } @@ -932,6 +1100,13 @@ t_stat xq_process_mop(CTLR* xq) break; case 9: /* Mop Read/Clear Counters */ break; + case 10: /* DELQA-PLUS Board ROM Version */ + if (xq->var->type == XQ_T_DELQA_PLUS) { + uint16 Delqa_Plus_ROM_Version[3] = {2, 0, 0}; /* 2.0.0 */ + wstatus = Map_WriteB(address, sizeof(Delqa_Plus_ROM_Version), (uint8*) Delqa_Plus_ROM_Version); + if (wstatus) return xq_nxm_error(xq); + } + break; } /* switch */ /* process next meb */ @@ -947,11 +1122,16 @@ t_stat xq_process_setup(CTLR* xq) int count = 0; float secs; t_stat status; + uint32 saved_debug = xq->dev->dctrl; ETH_MAC zeros = {0, 0, 0, 0, 0, 0}; ETH_MAC filters[XQ_FILTER_MAX + 1]; sim_debug(DBG_TRC, xq->dev, "xq_process_setup()\n"); + /* temporarily turn on Ethernet debugging if setup debugging is enabled */ + if (xq->dev->dctrl & DBG_SET) + xq->dev->dctrl |= DBG_ETH; + /* extract filter addresses from setup packet */ memset(xq->var->setup.macs, '\0', sizeof(xq->var->setup.macs)); for (i = 0; i < 7; i++) @@ -1018,17 +1198,16 @@ t_stat xq_process_setup(CTLR* xq) case 7: secs = 64 * 60; break; /* 64 minutes */ } xq->var->sanity.quarter_secs = (int) (secs * 4); - xq->var->sanity.max = (int) (secs * xq->var->poll); } /* finalize sanity timer state */ - xq->var->sanity.timer = xq->var->sanity.max; if (xq->var->sanity.enabled != 2) { if (xq->var->csr & XQ_CSR_SE) xq->var->sanity.enabled = 1; else xq->var->sanity.enabled = 0; } + xq_reset_santmr(xq); /* set ethernet filter */ /* memcpy (filters[count++], xq->mac, sizeof(ETH_MAC)); */ @@ -1044,8 +1223,10 @@ t_stat xq_process_setup(CTLR* xq) /* mark setup block valid */ xq->var->setup.valid = 1; - if (sim_deb && (xq->dev->dctrl & DBG_SET)) - xq_debug_setup(xq); + xq_debug_setup(xq); + + xq->dev->dctrl = saved_debug; /* restore original debugging */ + return SCPE_OK; } @@ -1054,6 +1235,9 @@ t_stat xq_process_setup(CTLR* xq) The DELQA manual does not explicitly state whether or not multiple packets can be written in one transmit operation, so a maximum of 1 packet is assumed. + + MP: Hmmm... Figure 3-1 on page 3-3 step 6 says that descriptors will be processed + until the end of the list is found. */ t_stat xq_process_xbdl(CTLR* xq) @@ -1104,7 +1288,7 @@ t_stat xq_process_xbdl(CTLR* xq) /* add to transmit buffer, making sure it's not too big */ if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) - b_length = sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len; + b_length = (uint16)(sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len); rstatus = Map_ReadB(address, b_length, &xq->var->write_buffer.msg[xq->var->write_buffer.len]); if (rstatus) return xq_nxm_error(xq); xq->var->write_buffer.len += b_length; @@ -1145,14 +1329,14 @@ t_stat xq_process_xbdl(CTLR* xq) status = eth_write(xq->var->etherface, &xq->var->write_buffer, xq->var->wcallback); if (status != SCPE_OK) /* not implemented or unattached */ xq_write_callback(xq, 1); /* fake failure */ -#if 0 - else - xq_svc(&xq->unit[0]); /* service any received data */ -#endif + else { + if (xq->var->coalesce_latency == 0) + xq_svc(&xq->unit[0]); /* service any received data */ + } sim_debug(DBG_WRN, xq->dev, "XBDL completed processing write\n"); - return SCPE_OK; } /* loopback/non-loopback */ + } else { /* not at end-of-message */ sim_debug(DBG_WRN, xq->dev, "XBDL processing implicit chain buffer segment\n"); @@ -1230,12 +1414,254 @@ t_stat xq_dispatch_xbdl(CTLR* xq) return status; } +t_stat xq_process_turbo_rbdl(CTLR* xq) +{ + int i; + t_stat status; + int descriptors_consumed = 0; + uint32 rdra = (xq->var->init.rdra_h << 16) | xq->var->init.rdra_l; + + sim_debug(DBG_TRC, xq->dev, "xq_process_turbo_rbdl()\n"); + + if ((xq->var->srr & XQ_SRR_RESP) != XQ_SRR_STRT) + return SCPE_OK; + + /* Process descriptors in the receive ring while the're available and we have packets */ + do { + uint32 address; + uint16 b_length, rbl; + ETH_ITEM* item; + uint8* rbuf; + + /* stop processing when nothing in read queue */ + if (!xq->var->ReadQ.count) + break; + + i = xq->var->rbindx; + + /* Get receive descriptor from memory */ + status = Map_ReadW (rdra+i*sizeof(xq->var->rring[i]), sizeof(xq->var->rring[i]), (uint16 *)&xq->var->rring[i]); + if (status != SCPE_OK) + return xq_nxm_error(xq); + + /* Done if Buffer not Owned */ + if (xq->var->rring[i].rmd3 & XQ_TMD3_OWN) + break; + + ++descriptors_consumed; + + /* Update ring index */ + xq->var->rbindx = (xq->var->rbindx + 1) % XQ_TURBO_RC_BCNT; + + address = ((xq->var->rring[i].hadr & 0x3F ) << 16) | xq->var->rring[i].ladr; + b_length = ETH_FRAME_SIZE; + + item = &xq->var->ReadQ.item[xq->var->ReadQ.head]; + rbl = item->packet.len + ETH_CRC_SIZE; + rbuf = item->packet.msg; + + /* see if packet must be size-adjusted or is splitting */ + if (item->packet.used) { + int used = item->packet.used; + rbl -= used; + rbuf = &item->packet.msg[used]; + } else { + /* adjust runt packets */ + if (rbl < ETH_MIN_PACKET) { + xq->var->stats.runt += 1; + sim_debug(DBG_WRN, xq->dev, "Runt detected, size = %d\n", rbl); + /* pad runts with zeros up to minimum size - this allows "legal" (size - 60) + processing of those weird short ARP packets that seem to occur occasionally */ + memset(&item->packet.msg[rbl], 0, ETH_MIN_PACKET-rbl); + rbl = ETH_MIN_PACKET; + }; + + /* adjust oversized packets */ + if (rbl > ETH_FRAME_SIZE) { + xq->var->stats.giant += 1; + sim_debug(DBG_WRN, xq->dev, "Giant detected, size=%d\n", rbl); + /* trim giants down to maximum size - no documentation on how to handle the data loss */ + item->packet.len = ETH_MAX_PACKET; + rbl = ETH_FRAME_SIZE; + }; + }; + + /* make sure entire packet fits in buffer - if not, will need to split into multiple buffers */ + if (rbl > b_length) + rbl = b_length; + item->packet.used += rbl; + + /* send data to host */ + status = Map_WriteB(address, rbl, rbuf); + if (status != SCPE_OK) + return xq_nxm_error(xq); + + /* set receive size into RBL - RBL<10:8> maps into Status1<10:8>, + RBL<7:0> maps into Status2<7:0>, and Status2<15:8> (copy) */ + xq->var->rring[i].rmd0 = 0; + xq->var->rring[i].rmd1 = rbl; + xq->var->rring[i].rmd2 = XQ_RMD2_RON | XQ_RMD2_TON; + if (0 == (item->packet.used - rbl)) + xq->var->rring[i].rmd0 |= XQ_RMD0_STP; /* Start of Packet */ + if (item->packet.used == (item->packet.len + ETH_CRC_SIZE)) + xq->var->rring[i].rmd0 |= XQ_RMD0_ENP; /* End of Packet */ + + if (xq->var->ReadQ.loss) { + xq->var->rring[i].rmd2 |= XQ_RMD2_MIS; + sim_debug(DBG_WRN, xq->dev, "ReadQ overflow!\n"); + xq->var->stats.dropped += xq->var->ReadQ.loss; + xq->var->ReadQ.loss = 0; /* reset loss counter */ + } + + Map_ReadW (rdra+(uint32)(((char *)(&xq->var->rring[xq->var->rbindx].rmd3))-((char *)&xq->var->rring)), sizeof(xq->var->rring[xq->var->rbindx].rmd3), (uint16 *)&xq->var->rring[xq->var->rbindx].rmd3); + if (xq->var->rring[xq->var->rbindx].rmd3 & XQ_RMD3_OWN) + xq->var->rring[i].rmd2 |= XQ_RMD2_EOR; + + /* Update receive descriptor in memory (only done after we've processed the contents) */ + /* Note: We're updating all but the end of the descriptor (which we never change) */ + /* AND the driver will be allowed to change once the changed tmd3 (ownership) */ + /* is noted so we avoid walking on its changes */ + xq->var->rring[i].rmd3 |= XQ_TMD3_OWN; /* Return Descriptor to Driver */ + status = Map_WriteW (rdra+i*sizeof(xq->var->rring[i]), sizeof(xq->var->rring[i])-8, (uint16 *)&xq->var->rring[i]); + if (status != SCPE_OK) + return xq_nxm_error(xq); + + /* remove packet from queue */ + if (item->packet.used >= item->packet.len) + ethq_remove(&xq->var->ReadQ); + } while (0 == (xq->var->rring[xq->var->rbindx].rmd3 & XQ_RMD3_OWN)); + + if (xq->var->rring[xq->var->rbindx].rmd3 & XQ_RMD3_OWN) + sim_debug(DBG_WRN, xq->dev, "xq_process_turbo_rbdl() - receive ring full\n"); + + if (descriptors_consumed) + /* Interrupt for Packet Reception Completion */ + xq_setint(xq); + + return SCPE_OK; +} + +t_stat xq_process_turbo_xbdl(CTLR* xq) +{ + int i; + t_stat status; + int descriptors_consumed = 0; + uint32 tdra = (xq->var->init.tdra_h << 16) | xq->var->init.tdra_l; + + sim_debug(DBG_TRC, xq->dev, "xq_process_turbo_xbdl()\n"); + + if ((xq->var->srr & XQ_SRR_RESP) != XQ_SRR_STRT) + return SCPE_OK; + + /* clear transmit buffer */ + xq->var->write_buffer.len = 0; + + /* Process each descriptor in the transmit ring */ + do { + uint32 address; + uint16 b_length; + + i = xq->var->tbindx; + + /* Get transmit descriptor from memory */ + status = Map_ReadW (tdra+i*sizeof(xq->var->xring[i]), sizeof(xq->var->xring[i]), (uint16 *)&xq->var->xring[i]); + if (status != SCPE_OK) + return xq_nxm_error(xq); + + if (xq->var->xring[i].tmd3 & XQ_TMD3_OWN) + break; + + /* Update ring index */ + xq->var->tbindx = (xq->var->tbindx + 1) % XQ_TURBO_XM_BCNT; + + ++descriptors_consumed; + address = ((xq->var->xring[i].hadr & 0x3F ) << 16) | xq->var->xring[i].ladr; + b_length = (xq->var->xring[i].tmd3 & XQ_TMD3_BCT); + + /* add to transmit buffer, making sure it's not too big */ + if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) + b_length = (uint16)(sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len); + status = Map_ReadB(address, b_length, &xq->var->write_buffer.msg[xq->var->write_buffer.len]); + if (status != SCPE_OK) + return xq_nxm_error(xq); + + xq->var->write_buffer.len += b_length; + if (!(xq->var->xring[i].tmd3 & XQ_TMD3_FOT)) { + /* Process Loopback if in Loopback mode */ + if (xq->var->init.mode & XQ_IN_MO_LOP) { + if ((xq->var->init.mode & XQ_IN_MO_INT) || (xq->var->etherface)) { + /* put packet in read buffer */ + ethq_insert (&xq->var->ReadQ, 1, &xq->var->write_buffer, 0); + status = SCPE_OK; + } else { + /* External loopback fails when not connected */ + status = SCPE_NOFNC; + } + } else + status = eth_write(xq->var->etherface, &xq->var->write_buffer, NULL); + + xq->var->stats.xmit += 1; + if (status != SCPE_OK) { /* not implemented or unattached */ + sim_debug(DBG_WRN, xq->dev, "Packet Write Error!\n"); + xq->var->stats.fail += 1; + xq->var->xring[i].tmd0 = XQ_TMD0_ERR1; + xq->var->xring[i].tmd1 = 100 + xq->var->write_buffer.len * 8; /* arbitrary value */ + xq->var->xring[i].tmd1 |= XQ_TMD1_LCA; + } else { + if (DBG_PCK & xq->dev->dctrl) + eth_packet_trace_ex(xq->var->etherface, xq->var->write_buffer.msg, xq->var->write_buffer.len, "xq-write", DBG_DAT & xq->dev->dctrl, DBG_PCK); + xq->var->xring[i].tmd0 = 0; + xq->var->xring[i].tmd1 = 100 + xq->var->write_buffer.len * 8; /* arbitrary value */ + } + sim_debug(DBG_WRN, xq->dev, "XBDL completed processing write\n"); + /* clear transmit buffer */ + xq->var->write_buffer.len = 0; + xq->var->xring[i].tmd2 = XQ_TMD2_RON | XQ_TMD2_TON; + } + + Map_ReadW (tdra+(uint32)(((char *)(&xq->var->xring[xq->var->tbindx].tmd3))-((char *)&xq->var->xring)), sizeof(xq->var->xring[xq->var->tbindx].tmd3), (uint16 *)&xq->var->xring[xq->var->tbindx].tmd3); + if (xq->var->xring[xq->var->tbindx].tmd3 & XQ_TMD3_OWN) + xq->var->xring[i].tmd2 |= XQ_TMD2_EOR; + + /* Update transmit descriptor in memory (only done after we've processed the contents) */ + /* Note: We're updating all but the end of the descriptor (which we never change) */ + /* AND the driver will be allowed to change once the changed tmd3 (ownership) */ + /* is noted so we avoid walking on its changes */ + xq->var->xring[i].tmd3 |= XQ_TMD3_OWN; /* Return Descriptor to Driver */ + status = Map_WriteW (tdra+i*sizeof(xq->var->xring[i]), sizeof(xq->var->xring[i])-8, (uint16 *)&xq->var->xring[i]); + if (status != SCPE_OK) + return xq_nxm_error(xq); + + } while (0 == (xq->var->xring[xq->var->tbindx].tmd3 & XQ_TMD3_OWN)); + + if (descriptors_consumed) { + + /* Interrupt for Packet Transmission Completion */ + xq_setint(xq); + + if (xq->var->coalesce_latency == 0) + xq_svc(&xq->unit[0]); /* service any received data */ + } else { + /* There appears to be a bug in the VMS SCS/XQ driver when it uses chained + buffers to transmit a packet. It updates the transmit buffer ring in the + correct order (i.e. clearing the ownership on the last packet segment + first), but it writes a transmit request to the ARQR register after adjusting + the ownership of EACH buffer piece. This results in us being awakened once + and finding nothing to do. We ignore this and the next write the ARQR will + properly cause the packet transmission. + */ + sim_debug(DBG_WRN, xq->dev, "xq_process_turbo_xbdl() - Nothing to Transmit\n"); + } + + return status; +} + t_stat xq_process_loopback(CTLR* xq, ETH_PACK* pack) { - ETH_PACK reply; - ETH_MAC physical_address; + ETH_PACK response; + ETH_MAC *physical_address; t_stat status; - int offset = pack->msg[14] | (pack->msg[15] << 8); + int offset = 16 + (pack->msg[14] | (pack->msg[15] << 8)); int function = pack->msg[offset] | (pack->msg[offset+1] << 8); sim_debug(DBG_TRC, xq->dev, "xq_process_loopback()\n"); @@ -1243,19 +1669,27 @@ t_stat xq_process_loopback(CTLR* xq, ETH_PACK* pack) if (function != 2 /*forward*/) return SCPE_NOFNC; - /* create reply packet */ - memcpy (&reply, pack, sizeof(ETH_PACK)); - memcpy (physical_address, xq->var->setup.valid ? xq->var->setup.macs[0] : xq->var->mac, sizeof(ETH_MAC)); - memcpy (&reply.msg[0], &reply.msg[offset+2], sizeof(ETH_MAC)); - memcpy (&reply.msg[6], physical_address, sizeof(ETH_MAC)); - memcpy (&reply.msg[offset+2], physical_address, sizeof(ETH_MAC)); - reply.msg[offset] = 0x01; - offset += 8; - reply.msg[14] = offset & 0xFF; - reply.msg[15] = (offset >> 8) & 0xFF; + /* create forward response packet */ + memcpy (&response, pack, sizeof(ETH_PACK)); + if (xq->var->mode == XQ_T_DELQA_PLUS) + physical_address = &xq->var->init.phys; + else + if (xq->var->setup.valid) + physical_address = &xq->var->setup.macs[0]; + else + physical_address = &xq->var->mac; + memcpy (&response.msg[0], &response.msg[offset+2], sizeof(ETH_MAC)); + memcpy (&response.msg[6], physical_address, sizeof(ETH_MAC)); + offset += 8 - 16; /* Account for the Ethernet Header and Offset value in this number */ + response.msg[14] = offset & 0xFF; + response.msg[15] = (offset >> 8) & 0xFF; - /* send reply packet */ - status = eth_write(xq->var->etherface, &reply, NULL); + /* send response packet */ + status = eth_write(xq->var->etherface, &response, NULL); + ++xq->var->stats.loop; + + if (DBG_PCK & xq->dev->dctrl) + eth_packet_trace_ex(xq->var->etherface, response.msg, response.len, ((function == 1) ? "xq-loopbackreply" : "xq-loopbackforward"), DBG_DAT & xq->dev->dctrl, DBG_PCK); return status; } @@ -1327,7 +1761,11 @@ t_stat xq_process_local (CTLR* xq, ETH_PACK* pack) void xq_read_callback(CTLR* xq, int status) { xq->var->stats.recv += 1; - if (xq->var->csr & XQ_CSR_RE) { /* receiver enabled */ + + if (DBG_PCK & xq->dev->dctrl) + eth_packet_trace_ex(xq->var->etherface, xq->var->read_buffer.msg, xq->var->read_buffer.len, "xq-recvd", DBG_DAT & xq->dev->dctrl, DBG_PCK); + + if ((xq->var->csr & XQ_CSR_RE) || (xq->var->mode == XQ_T_DELQA_PLUS)) { /* receiver enabled */ /* process any packets locally that can be */ t_stat status = xq_process_local (xq, &xq->var->read_buffer); @@ -1336,6 +1774,7 @@ void xq_read_callback(CTLR* xq, int status) if (status != SCPE_OK) ethq_insert(&xq->var->ReadQ, 2, &xq->var->read_buffer, status); } else { + xq->var->stats.dropped += 1; sim_debug(DBG_WRN, xq->dev, "packet received with receiver disabled\n"); } } @@ -1356,6 +1795,13 @@ void xq_sw_reset(CTLR* xq) int i; sim_debug(DBG_TRC, xq->dev, "xq_sw_reset()\n"); + ++xq->var->stats.reset; + + /* Return DELQA-T to DELQA Normal mode */ + if (xq->var->type == XQ_T_DELQA_PLUS) { + xq->var->mode = XQ_T_DELQA; + xq->var->iba = xq->var->srr = 0; + } /* reset csr bits */ xq_csr_set_clr(xq, set_bits, (uint16) ~set_bits); @@ -1384,6 +1830,10 @@ void xq_sw_reset(CTLR* xq) memcpy (filters[count++], xq->var->setup.macs[i], sizeof(ETH_MAC)); eth_filter (xq->var->etherface, count, filters, xq->var->setup.multicast, xq->var->setup.promiscuous); } + + /* Stop receive polling until the receiver is reenabled */ + xq_stop_receiver(xq); + } /* write registers: */ @@ -1398,11 +1848,25 @@ t_stat xq_wr_var(CTLR* xq, int32 data) xq->var->var = (data & XQ_VEC_IV); break; case XQ_T_DELQA: + case XQ_T_DELQA_PLUS: xq->var->var = (xq->var->var & XQ_VEC_RO) | (data & XQ_VEC_RW); /* if switching to DEQNA-LOCK mode clear VAR<14:10> */ - if (~xq->var->var & XQ_VEC_MS) + if (~xq->var->var & XQ_VEC_MS) { + xq->var->mode = XQ_T_DEQNA; xq->var->var &= ~(XQ_VEC_OS | XQ_VEC_RS | XQ_VEC_ST); + } else { + xq->var->mode = XQ_T_DELQA; + } + + /* if Self Test is on, turn it off to signal completion */ + if (xq->var->var & XQ_VEC_RS) { + xq->var->var &= ~XQ_VEC_RS; + if (!xq->var->etherface) + xq->var->var |= XQ_VEC_S1; /* Indicate No Network Connection */ + else + xq->var->var &= ~XQ_VEC_ST; /* Set success Status */ + } break; } @@ -1619,15 +2083,21 @@ t_stat xq_wr_csr(CTLR* xq, int32 data) return SCPE_OK; } -#if 0 /* controller should ALWAYS have an active timer if enabled (for HW sanity) */ - /* start/stop receive timer when RE transitions */ - if ((xq->var->csr ^ data) & XQ_CSR_RE) { - if (data & XQ_CSR_RE) - sim_activate(&xq->unit[0], clock_cosched (tmxr_poll)); - else - sim_cancel(&xq->unit[0]); + /* start receiver when RE transitions to set */ + if (~xq->var->csr & XQ_CSR_RE & data) { + sim_debug(DBG_REG, xq->dev, "xq_wr_csr(data=0x%08X) - receiver started\n", data); + + /* start the read service timer or enable asynch reading as appropriate */ + xq_start_receiver(xq); + } + + /* stop receiver when RE transitions to clear */ + if (xq->var->csr & XQ_CSR_RE & ~data) { + sim_debug(DBG_REG, xq->dev, "xq_wr_csr(data=0x%08X) - receiver stopped\n", data); + + /* stop the read service timer or disable asynch reading as appropriate */ + xq_stop_receiver(xq); } -#endif /* update CSR bits */ xq_csr_set_clr (xq, set_bits, clr_bits); @@ -1641,37 +2111,194 @@ t_stat xq_wr_csr(CTLR* xq, int32 data) return SCPE_OK; } +void xq_start_receiver(CTLR* xq) +{ + if (!xq->var->etherface) + return; + + /* start the read service timer or enable asynch reading as appropriate */ + if (xq->var->must_poll) + sim_activate(xq->unit, (sim_idle_enab ? tmxr_poll : (tmr_poll*clk_tps)/xq->var->poll)); + else + if ((xq->var->poll == 0) || (xq->var->mode == XQ_T_DELQA_PLUS)) + eth_set_async(xq->var->etherface, xq->var->coalesce_latency_ticks); + else + sim_activate(xq->unit, (sim_idle_enab ? tmxr_poll : (tmr_poll*clk_tps)/xq->var->poll)); +} + +void xq_stop_receiver(CTLR* xq) +{ + sim_cancel(&xq->unit[0]); /* Stop Receiving */ + if (xq->var->etherface) + eth_clr_async(xq->var->etherface); +} + +t_stat xq_wr_srqr(CTLR* xq, int32 data) +{ + uint16 set_bits = data & XQ_SRQR_RW; /* set RW set bits */ + + sim_debug(DBG_REG, xq->dev, "xq_wr_srqr(data=0x%08X)\n", data); + + xq->var->srr = set_bits; + + switch (set_bits) { + case XQ_SRQR_STRT: { + t_stat status; + + xq->var->stats.setup += 1; + /* Get init block from memory */ + status = Map_ReadW (xq->var->iba, sizeof(xq->var->init), (uint16 *)&xq->var->init); + if (SCPE_OK != status) { + xq_nxm_error (xq); + } else { + uint32 saved_debug = xq->dev->dctrl; + + /* temporarily turn on Ethernet debugging if setup debugging is enabled */ + if (xq->dev->dctrl & DBG_SET) + xq->dev->dctrl |= DBG_ETH; + + xq_debug_turbo_setup(xq); + + xq->dib->vec = xq->var->init.vector + VEC_Q; + xq->var->tbindx = xq->var->rbindx = 0; + if ((xq->var->sanity.enabled) && (xq->var->init.options & XQ_IN_OP_HIT)) { + xq->var->sanity.quarter_secs = 4*xq->var->init.hit_timeout; + } + xq->var->icr = xq->var->init.options & XQ_IN_OP_INT; + status = eth_filter_hash (xq->var->etherface, 1, &xq->var->init.phys, 0, xq->var->init.mode & XQ_IN_MO_PRO, &xq->var->init.hash_filter); + + xq->dev->dctrl = saved_debug; /* restore original debugging */ + } + /* start the read service timer or enable asynch reading as appropriate */ + xq_start_receiver(xq); + break; + } + case XQ_SRQR_STOP: + xq_stop_receiver(xq); + break; + default: + break; + } + + /* All Writes to SRQR reset the Host Inactivity Timer */ + xq_reset_santmr(xq); + + /* Interrupt after this synchronous request completion */ + xq_setint(xq); + + return SCPE_OK; +} + +t_stat xq_wr_arqr(CTLR* xq, int32 data) +{ + sim_debug(DBG_REG, xq->dev, "xq_wr_arqr(data=0x%08X)\n", data); + + /* initiate transmit activity when requested */ + if (XQ_ARQR_TRQ & data) { + xq_process_turbo_xbdl (xq); + } + /* initiate transmit activity when requested */ + if (XQ_ARQR_RRQ & data) { + xq_process_turbo_rbdl (xq); + } + + /* reset controller when requested */ + if (XQ_ARQR_SR & data) { + xq_sw_reset(xq); + } + + /* All Writes to ARQR reset the Host Inactivity Timer */ + xq_reset_santmr(xq); + + return SCPE_OK; +} + +t_stat xq_wr_icr(CTLR* xq, int32 data) +{ + uint16 old_icr = xq->var->icr; + + sim_debug(DBG_REG, xq->dev, "xq_wr_icr(data=0x%08X)\n", data); + + xq->var->icr = data & XQ_ICR_ENA; + + if (xq->var->icr && !old_icr && xq->var->pending_interrupt) + xq_setint(xq); + + return SCPE_OK; +} + t_stat xq_wr(int32 data, int32 PA, int32 access) { t_stat status; CTLR* xq = xq_pa2ctlr(PA); int index = (PA >> 1) & 07; /* word index */ - sim_debug(DBG_REG, xq->dev, "xq_wr(data=0x%08X, PA=0x%08X[%s], access=%d)\n", data, PA, xq_xmit_regnames[index], access); + sim_debug(DBG_REG, xq->dev, "xq_wr(data=0x%08X, PA=0x%08X[%s], access=%d)\n", data, PA, ((xq->var->mode == XQ_T_DELQA_PLUS) ? xqt_xmit_regnames[index] : xq_xmit_regnames[index]), access); - switch (index) { - case 0: /* these should not be written */ - case 1: + switch (xq->var->mode) { + case XQ_T_DELQA_PLUS: + switch (index) { + case 0: /* IBAL */ + xq->var->iba = (xq->var->iba & 0xFFFF0000) | (data & 0xFFFF); + break; + case 1: /* IBAH */ + xq->var->iba = (xq->var->iba & 0xFFFF) | ((data & 0xFFFF) << 16); + break; + case 2: /* ICR */ + status = xq_wr_icr(xq, data); + break; + case 3: + break; + case 4: /* SRQR */ + status = xq_wr_srqr(xq, data); + break; + case 5: + break; + case 6: + break; + case 7: /* ARQR */ + status = xq_wr_arqr(xq, data); + break; + } break; - case 2: /* receive bdl low bits */ - xq->var->rbdl[0] = data; - break; - case 3: /* receive bdl high bits */ - xq->var->rbdl[1] = data; - status = xq_dispatch_rbdl(xq); /* start receive operation */ - break; - case 4: /* transmit bdl low bits */ - xq->var->xbdl[0] = data; - break; - case 5: /* transmit bdl high bits */ - xq->var->xbdl[1] = data; - status = xq_dispatch_xbdl(xq); /* start transmit operation */ - break; - case 6: /* vector address register */ - status = xq_wr_var(xq, data); - break; - case 7: /* control and status register */ - status = xq_wr_csr(xq, data); + default: /* DEQNA, DELQA Normal */ + switch (index) { + case 0: /* IBAL/XCR0 */ /* these should only be written on a DELQA-T */ + if (xq->var->type == XQ_T_DELQA_PLUS) + xq->var->iba = (xq->var->iba & 0xFFFF0000) | (data & 0xFFFF); + break; + case 1: /* IBAH/XCR1 */ + if (xq->var->type == XQ_T_DELQA_PLUS) { + if (((xq->var->iba & 0xFFFF) == 0x0BAF) && (data == 0xFF00)) { + xq->var->mode = XQ_T_DELQA_PLUS; + xq->var->srr = XQ_SRR_TRBO; + sim_cancel(xq->unit); /* Turn off receive processing until explicitly enabled */ + eth_clr_async(xq->var->etherface); + } + xq->var->iba = (xq->var->iba & 0xFFFF) | ((data & 0xFFFF) << 16); + } + break; + case 2: /* receive bdl low bits */ + xq->var->rbdl[0] = data; + break; + case 3: /* receive bdl high bits */ + xq->var->rbdl[1] = data; + status = xq_dispatch_rbdl(xq); /* start receive operation */ + break; + case 4: /* transmit bdl low bits */ + xq->var->xbdl[0] = data; + break; + case 5: /* transmit bdl high bits */ + xq->var->xbdl[1] = data; + status = xq_dispatch_xbdl(xq); /* start transmit operation */ + break; + case 6: /* vector address register */ + status = xq_wr_var(xq, data); + break; + case 7: /* control and status register */ + status = xq_wr_csr(xq, data); + break; + } break; } return SCPE_OK; @@ -1694,9 +2321,12 @@ t_stat xq_reset(DEVICE* dptr) switch (xq->var->type) { case XQ_T_DEQNA: xq->var->var = 0; + xq->var->mode = XQ_T_DEQNA; break; case XQ_T_DELQA: + case XQ_T_DELQA_PLUS: xq->var->var = XQ_VEC_MS | XQ_VEC_OS; + xq->var->mode = XQ_T_DELQA; break; } xq->dib->vec = 0; @@ -1717,18 +2347,25 @@ t_stat xq_reset(DEVICE* dptr) /* reset ethernet interface */ if (xq->var->etherface) { + /* restore filter on ROM mac address */ status = eth_filter (xq->var->etherface, 1, &xq->var->mac, 0, 0); xq_csr_set_clr(xq, XQ_CSR_OK, 0); /* start service timer */ - sim_activate_abs(&xq->unit[0], tmxr_poll); + sim_activate_abs(&xq->unit[1], (tmr_poll * clk_tps) / 4); + + /* stop the receiver */ + eth_clr_async(xq->var->etherface); } + /* stop the receiver */ + sim_cancel(xq->unit); + /* set hardware sanity controls */ if (xq->var->sanity.enabled) { xq->var->sanity.quarter_secs = XQ_HW_SANITY_SECS * 4/*qsec*/; - xq->var->sanity.max = XQ_HW_SANITY_SECS * xq->var->poll; } + return SCPE_OK; } @@ -1739,7 +2376,7 @@ void xq_reset_santmr(CTLR* xq) sim_debug(DBG_SAN, xq->dev, "SANITY TIMER RESETTING, qsecs: %d\n", xq->var->sanity.quarter_secs); /* reset sanity countdown timer to max count */ - xq->var->sanity.timer = xq->var->sanity.max; + xq->var->sanity.timer = xq->var->sanity.quarter_secs; } } @@ -1750,7 +2387,7 @@ t_stat xq_boot_host(CTLR* xq) The manual says the hardware should force the Qbus BDCOK low for 3.6 microseconds, which will cause the host to reboot. - Since the SIMH Qbus emulator does not have this functionality, we call + Since the SIMH Qbus emulator does not have this functionality, we return a special STOP_ code, and let the CPU stop dispatch routine decide what the appropriate cpu-specific behavior should be. */ @@ -1765,7 +2402,16 @@ t_stat xq_system_id (CTLR* xq, const ETH_MAC dest, uint16 receipt_id) t_stat status; sim_debug(DBG_TRC, xq->dev, "xq_system_id()\n"); - if (xq->var->type != XQ_T_DELQA) /* DELQA-only function */ + + /* reset system ID counter for next event */ + xq->var->idtmr = XQ_SYSTEM_ID_SECS * 4; + + if (xq->var->coalesce_latency) { + /* Adjust latency ticks based on calibrated timer values */ + xq->var->coalesce_latency_ticks = (tmr_poll * clk_tps * xq->var->coalesce_latency) / 1000000; + } + + if (xq->var->type == XQ_T_DEQNA) /* DELQA-only function */ return SCPE_NOFNC; memset (&system_id, 0, sizeof(system_id)); @@ -1811,11 +2457,16 @@ t_stat xq_system_id (CTLR* xq, const ETH_MAC dest, uint16 receipt_id) msg[41] = 0x00; /* type */ msg[42] = 0x01; /* length */ msg[43] = 0x11; /* value (0x11=DELQA) */ + if (xq->var->type == XQ_T_DELQA_PLUS) /* DELQA-T has different Device ID */ + msg[43] = 0x4B; /* value (0x4B(75)=DELQA-T) */ /* write system id */ system_id.len = 60; status = eth_write(xq->var->etherface, &system_id, NULL); + if (DBG_PCK & xq->dev->dctrl) + eth_packet_trace_ex(xq->var->etherface, system_id.msg, system_id.len, "xq-systemid", DBG_DAT & xq->dev->dctrl, DBG_PCK); + return status; } @@ -1827,44 +2478,59 @@ t_stat xq_svc(UNIT* uptr) CTLR* xq = xq_unit2ctlr(uptr); /* if the receiver is enabled */ - if (xq->var->csr & XQ_CSR_RE) { + if ((xq->var->mode == XQ_T_DELQA_PLUS) || (xq->var->csr & XQ_CSR_RE)) { t_stat status; - int queue_size; /* First pump any queued packets into the system */ - if ((xq->var->ReadQ.count > 0) && (~xq->var->csr & XQ_CSR_RL)) - status = xq_process_rbdl(xq); + if ((xq->var->ReadQ.count > 0) && ((xq->var->mode == XQ_T_DELQA_PLUS) || (~xq->var->csr & XQ_CSR_RL))) + xq_process_rbdl(xq); /* Now read and queue packets that have arrived */ - /* This is repeated as long as they are available and we have room */ - do - { - queue_size = xq->var->ReadQ.count; + /* This is repeated as long as they are available */ + do { /* read a packet from the ethernet - processing is via the callback */ status = eth_read (xq->var->etherface, &xq->var->read_buffer, xq->var->rcallback); - } while (queue_size != xq->var->ReadQ.count); + } while (status); /* Now pump any still queued packets into the system */ - if ((xq->var->ReadQ.count > 0) && (~xq->var->csr & XQ_CSR_RL)) - status = xq_process_rbdl(xq); + if ((xq->var->ReadQ.count > 0) && ((xq->var->mode == XQ_T_DELQA_PLUS) || (~xq->var->csr & XQ_CSR_RL))) + xq_process_rbdl(xq); } + /* resubmit service timer */ + if ((xq->var->must_poll) || (xq->var->poll && (xq->var->mode != XQ_T_DELQA_PLUS))) + sim_activate(uptr, (sim_idle_enab ? tmxr_poll : (tmr_poll*clk_tps)/xq->var->poll)); + + return SCPE_OK; +} + +/* +** service routine - used for timer based activities +*/ +t_stat xq_tmrsvc(UNIT* uptr) +{ + CTLR* xq = xq_unit2ctlr(uptr); + /* has sanity timer expired? if so, reboot */ if (xq->var->sanity.enabled) if (--xq->var->sanity.timer <= 0) - xq_boot_host(xq); + if (xq->var->mode != XQ_T_DELQA_PLUS) + return xq_boot_host(xq); + else { /* DELQA-T Host Inactivity Timer expiration means switch out of DELQA-T mode */ + sim_debug(DBG_TRC, xq->dev, "xq_tmrsvc(DELQA-PLUS Host Inactivity Expired\n"); + xq->var->mode = XQ_T_DELQA; + xq->var->iba = xq->var->srr = 0; + xq->var->var = XQ_VEC_MS | XQ_VEC_OS; + } /* has system id timer expired? if so, do system id */ if (--xq->var->idtmr <= 0) { const ETH_MAC mop_multicast = {0xAB, 0x00, 0x00, 0x02, 0x00, 0x00}; xq_system_id(xq, mop_multicast, 0); - - /* reset system ID counter for next event */ - xq->var->idtmr = XQ_SYSTEM_ID_SECS * xq->var->poll; } /* resubmit service timer */ - sim_activate(&xq->unit[0], tmxr_poll); + sim_activate(uptr, (tmr_poll * clk_tps) / 4); return SCPE_OK; } @@ -1904,6 +2570,27 @@ t_stat xq_attach(UNIT* uptr, char* cptr) xq->var->etherface = 0; return status; } + if (xq->var->poll == 0) { + status = eth_set_async(xq->var->etherface, xq->var->coalesce_latency_ticks); + if (status != SCPE_OK) { + free(tptr); + free(xq->var->etherface); + xq->var->etherface = 0; + return status; + } + xq->var->must_poll = 0; + } else { + xq->var->must_poll = (SCPE_OK != eth_clr_async(xq->var->etherface)); + } + if (SCPE_OK != eth_check_address_conflict (xq->var->etherface, &xq->var->mac)) { + char buf[32]; + + eth_mac_fmt(&xq->var->mac, buf); /* format ethernet mac address */ + printf("%s: MAC Address Conflict on LAN for address %s, change the MAC address to a unique value\n", xq->dev->name, buf); + if (sim_log) fprintf (sim_log, "%s: MAC Address Conflict on LAN for address %s, change the MAC address to a unique value\n", xq->dev->name, buf); + eth_close(xq->var->etherface); + return SCPE_NOATT; + } uptr->filename = tptr; uptr->flags |= UNIT_ATT; @@ -1930,8 +2617,9 @@ t_stat xq_detach(UNIT* uptr) free(uptr->filename); uptr->filename = NULL; uptr->flags &= ~UNIT_ATT; - /* cancel service timer */ + /* cancel service timers */ sim_cancel(&xq->unit[0]); + sim_cancel(&xq->unit[1]); } /* turn off transceiver power indicator */ @@ -1942,6 +2630,16 @@ t_stat xq_detach(UNIT* uptr) void xq_setint(CTLR* xq) { + if (xq->var->mode == XQ_T_DELQA_PLUS) { + if (!xq->var->icr) { + xq->var->pending_interrupt = 1; + return; + } + xq->var->pending_interrupt = 0; + } + + sim_debug(DBG_TRC, xq->dev, "xq_setint() - Generate Interrupt\n"); + xq->var->irq = 1; SET_INT(XQ); return; @@ -2030,12 +2728,16 @@ void xq_debug_setup(CTLR* xq) { int i; char buffer[20]; + + if (!(sim_deb && (xq->dev->dctrl & DBG_SET))) + return; + if (xq->var->write_buffer.msg[0]) - printf ("%s: setup> MOP info present!\n", xq->dev->name); + sim_debug(DBG_SET, xq->dev, "%s: setup> MOP info present!\n", xq->dev->name); for (i = 0; i < XQ_FILTER_MAX; i++) { eth_mac_fmt(&xq->var->setup.macs[i], buffer); - printf ("%s: setup> set addr[%d]: %s\n", xq->dev->name, i, buffer); + sim_debug(DBG_SET, xq->dev, "%s: setup> set addr[%d]: %s\n", xq->dev->name, i, buffer); } if (xq->var->write_buffer.len > 128) { @@ -2045,7 +2747,55 @@ void xq_debug_setup(CTLR* xq) if (len & XQ_SETUP_PM) strcat(buffer, "PM "); if (len & XQ_SETUP_LD) strcat(buffer, "LD "); if (len & XQ_SETUP_ST) strcat(buffer, "ST "); - printf ("%s: setup> Length [%d =0x%X, LD:%d, ST:%d] info: %s\n", + sim_debug(DBG_SET, xq->dev, "%s: setup> Length [%d =0x%X, LD:%d, ST:%d] info: %s\n", xq->dev->name, len, len, (len & XQ_SETUP_LD) >> 2, (len & XQ_SETUP_ST) >> 4, buffer); } } + +void xq_debug_turbo_setup(CTLR* xq) +{ + int i; + char buffer[64] = ""; + + if (!(sim_deb && (xq->dev->dctrl & DBG_SET))) + return; + + sim_debug(DBG_SET, xq->dev, "%s: setup> Turbo Initialization Block!\n", xq->dev->name); + + if (xq->var->init.mode & XQ_IN_MO_PRO) strcat(buffer, "PRO "); + if (xq->var->init.mode & XQ_IN_MO_INT) strcat(buffer, "INT "); + if (xq->var->init.mode & XQ_IN_MO_DRT) strcat(buffer, "DRC "); + if (xq->var->init.mode & XQ_IN_MO_DTC) strcat(buffer, "DTC "); + if (xq->var->init.mode & XQ_IN_MO_LOP) strcat(buffer, "LOP "); + sim_debug(DBG_SET, xq->dev, "%s: setup> set Mode: %s\n", xq->dev->name, buffer); + + eth_mac_fmt(&xq->var->init.phys, buffer); + sim_debug(DBG_SET, xq->dev, "%s: setup> set Physical MAC Address: %s\n", xq->dev->name, buffer); + + buffer[0] = '\0'; + for (i = 0; i < sizeof(xq->var->init.hash_filter); i++) + sprintf(&buffer[strlen(buffer)], "%02X ", xq->var->init.hash_filter[i]); + sim_debug(DBG_SET, xq->dev, "%s: setup> set Multicast Hash: %s\n", xq->dev->name, buffer); + + buffer[0] = '\0'; + if (xq->var->init.options & XQ_IN_OP_HIT) strcat(buffer, "HIT "); + if (xq->var->init.options & XQ_IN_OP_INT) strcat(buffer, "INT "); + sim_debug(DBG_SET, xq->dev, "%s: setup> set Options: %s\n", xq->dev->name, buffer); + + sim_debug(DBG_SET, xq->dev, "%s: setup> set Vector: %d =0x%X\n", + xq->dev->name, xq->var->init.vector, xq->var->init.vector); + + sim_debug(DBG_SET, xq->dev, "%s: setup> set Host Inactivity Timeout: %d seconds\n", + xq->dev->name, xq->var->init.hit_timeout); + + buffer[0] = '\0'; + for (i = 0; i < sizeof(xq->var->init.bootpassword); i++) + sprintf(&buffer[strlen(buffer)], "%02X ", xq->var->init.bootpassword[i]); + + sim_debug(DBG_SET, xq->dev, "%s: setup> set Boot Password: %s\n", xq->dev->name, buffer); + + sim_debug(DBG_SET, xq->dev, "%s: setup> set Receive Ring Buffer Address: %02X%04X\n", + xq->dev->name, xq->var->init.rdra_h, xq->var->init.rdra_l); + sim_debug(DBG_SET, xq->dev, "%s: setup> set Transmit Ring Buffer Address: %02X%04X\n", + xq->dev->name, xq->var->init.tdra_h, xq->var->init.tdra_l); +} diff --git a/PDP11/pdp11_xq.h b/PDP11/pdp11_xq.h index 737552c7..16327504 100644 --- a/PDP11/pdp11_xq.h +++ b/PDP11/pdp11_xq.h @@ -28,6 +28,14 @@ Modification history: + 03-Mar-08 MP Added DELQA-T (aka DELQA Plus) device emulation support. + 06-Feb-08 MP Added dropped frame statistics to record when the receiver discards + received packets due to the receiver being disabled, or due to the + XQ device's packet receive queue being full. Also removed the + filter statistic counter since there was no code which ever set it. + 29-Jan-08 MP Dynamically determine the timer polling rate based on the + calibrated tmr_poll and clk_tps values of the simulator. + 23-Jan-08 MP Added debugging support to display packet headers and packet data 07-Jul-05 RMS Removed extraneous externs 20-Jan-04 DTH Added new sanity timer and system id timer 19-Jan-04 DTH Added XQ_SERVICE_INTERVAL, poll @@ -84,12 +92,11 @@ extern int32 int_req[IPL_HLVL]; #define XQ_HW_SANITY_SECS 240 /* seconds before HW sanity timer expires */ #define XQ_MAX_CONTROLLERS 2 /* maximum controllers allowed */ -enum xq_type {XQ_T_DEQNA, XQ_T_DELQA}; +enum xq_type {XQ_T_DEQNA, XQ_T_DELQA, XQ_T_DELQA_PLUS}; struct xq_sanity { int enabled; /* sanity timer enabled? 2=HW, 1=SW, 0=off */ int quarter_secs; /* sanity timer value in 1/4 seconds */ - int max; /* maximum timeout (based on poll) */ int timer; /* countdown timer */ }; @@ -104,17 +111,132 @@ struct xq_setup { ETH_MAC macs[XQ_FILTER_MAX]; /* MAC addresses to respond to */ }; +struct xq_turbo_init_block { /* DELQA-T Initialization Block */ + uint16 mode; +#define XQ_IN_MO_PRO 0x8000 /* Promiscuous Mode */ +#define XQ_IN_MO_INT 0x0040 /* Internal Loopback Mode */ +#define XQ_IN_MO_DRT 0x0020 /* Disable Retry */ +#define XQ_IN_MO_DTC 0x0008 /* Disable Transmit CRC */ +#define XQ_IN_MO_LOP 0x0004 /* Loopback */ + ETH_MAC phys; /* Physical MAC Address */ + ETH_MULTIHASH hash_filter; /* 64bit LANCE Hash Filter for Multicast Address selection */ + uint16 rdra_l; + uint16 rdra_h; + uint16 tdra_l; + uint16 tdra_h; + uint16 options; +#define XQ_IN_OP_HIT 0x0002 /* Host Inactivity Timer Enable Flag */ +#define XQ_IN_OP_INT 0x0001 /* Interrupt Enable Flag*/ + uint16 vector; /* Interrupt Vector */ + uint16 hit_timeout; /* Host Inactivity Timer Timeout Value */ + uint8 bootpassword[6]; /* MOP Console Boot Password */ +}; + +/* DELQA-T Mode - Transmit Buffer Descriptor */ +struct transmit_buffer_descriptor { + uint16 tmd0; +#define XQ_TMD0_ERR1 0x4000 /* Error Summary. The OR of TMD1 (LC0, LCA, and RTR) */ +#define XQ_TMD0_MOR 0x1000 /* More than one retry on transmit */ +#define XQ_TMD0_ONE 0x0800 /* One retry on transmit */ +#define XQ_TMD0_DEF 0x0400 /* Deferral during transmit */ + uint16 tmd1; +#define XQ_TMD1_LCO 0x1000 /* Late collision on transmit - packet not transmitted */ +#define XQ_TMD1_LCA 0x0800 /* Loss of carrier on transmit - packet not transmitted */ +#define XQ_TMD1_RTR 0x0400 /* Retry error on transmit - packet not transmitted */ +#define XQ_TMD1_TDR 0x03FF /* Time Domain Reflectometry value */ + uint16 tmd2; +#define XQ_TMD2_ERR2 0x8000 /* Error Summary. The OR of TMD2 (BBL, CER, and MIS) */ +#define XQ_TMD2_BBL 0x4000 /* Babble error on transmit */ +#define XQ_TMD2_CER 0x2000 /* Collision error on transmit */ +#define XQ_TMD2_MIS 0x1000 /* Packet lost on receive */ +#define XQ_TMD2_EOR 0x0800 /* End Of Receive Ring Reached */ +#define XQ_TMD2_RON 0x0020 /* Receiver On */ +#define XQ_TMD2_TON 0x0010 /* Transmitter On */ + uint16 tmd3; +#define XQ_TMD3_OWN 0x8000 /* Ownership field. 0 = DELQA-T, 1 = Host Driver */ +#define XQ_TMD3_FOT 0x4000 /* First Of Two flag. 1 = first in chained, 0 = no chain or last in chain */ +#define XQ_TMD3_BCT 0x0FFF /* Byte Count */ + uint16 ladr; /* Low 16bits of Buffer Address */ + uint16 hadr; /* Most significant bits of the Buffer Address */ + uint16 hostuse1; + uint16 hostuse2; +}; +#define XQ_TURBO_XM_BCNT 12 /* Transmit Buffer Descriptor Count */ + +struct receive_buffer_descriptor { + uint16 rmd0; +#define XQ_RMD0_ERR3 0x4000 /* Error Summary. The OR of FRA, CRC, OFL and BUF */ +#define XQ_RMD0_FRA 0x2000 /* Framing error on receive */ +#define XQ_RMD0_OFL 0x1000 /* Overflow error on receive (Giant packet) */ +#define XQ_RMD0_CRC 0x0800 /* CRC error on receive */ +#define XQ_RMD0_BUF 0x0400 /* Internal device buffer error. Part of Giant packet lost */ +#define XQ_RMD0_STP 0x0200 /* Start of Packet Flag */ +#define XQ_RMD0_ENP 0x0100 /* End of Packet Flag */ + uint16 rmd1; +#define XQ_RMD1_MCNT 0x0FFF /* Message byte count (including CRC) */ + uint16 rmd2; +#define XQ_RMD2_ERR4 0x8000 /* Error Summary. The OR of RMD2 (RBL, CER, and MIS) */ +#define XQ_RMD2_BBL 0x4000 /* Babble error on transmit */ +#define XQ_RMD2_CER 0x2000 /* Collision error on transmit */ +#define XQ_RMD2_MIS 0x1000 /* Packet lost on receive */ +#define XQ_RMD2_EOR 0x0800 /* End Of Receive Ring Reached */ +#define XQ_RMD2_RON 0x0020 /* Receiver On */ +#define XQ_RMD2_TON 0x0010 /* Transmitter On */ + uint16 rmd3; +#define XQ_RMD3_OWN 0x8000 /* Ownership field. 0 = DELQA-T, 1 = Host Driver */ + uint16 ladr; /* Low 16bits of Buffer Address */ + uint16 hadr; /* Most significant bits of the Buffer Address */ + uint16 hostuse1; + uint16 hostuse2; +}; +#define XQ_TURBO_RC_BCNT 32 /* Receive Buffer Descriptor Count */ + struct xq_stats { int recv; /* received packets */ - int filter; /* filtered packets */ + int dropped; /* received packets dropped */ int xmit; /* transmitted packets */ int fail; /* transmit failed */ int runt; /* runts */ + int reset; /* reset count */ int giant; /* oversize packets */ int setup; /* setup packets */ int loop; /* loopback packets */ }; +#pragma pack(2) +struct xq_mop_counters { + uint16 seconds; /* Seconds since last zeroed */ + uint32 b_rcvd; /* Bytes Received */ + uint32 b_xmit; /* Bytes Transmitted */ + uint32 p_rcvd; /* Packets Received */ + uint32 p_xmit; /* Packets Transmitted */ + uint32 mb_rcvd; /* Multicast Bytes Received */ + uint32 mp_rcvd; /* Multicast Packets Received */ + uint32 p_x_col1; /* Packets Transmitted Initially Deferred */ + uint32 p_x_col2; /* Packets Transmitted after 2 attempts */ + uint32 p_x_col3; /* Packets Transmitted after 3+ attempts */ + uint16 p_x_fail; /* Transmit Packets Aborted (Send Failure) */ + uint16 p_x_f_bitmap; /* Transmit Packets Aborted (Send Failure) Bitmap */ +#define XQ_XF_RTRY 0x0001 /* Excessive Collisions */ +#define XQ_XF_LCAR 0x0002 /* Loss of Carrier */ +#define XQ_XF_MLEN 0x0010 /* Data Block Too Long */ +#define XQ_XF_LCOL 0x0020 /* Late Collision */ + uint16 p_r_fail; /* Packets received with Error (Receive Failure) */ + uint16 p_r_f_bitmap; /* Packets received with Error (Receive Failure) Bitmap */ +#define XQ_RF_CRC 0x0001 /* Block Check Error */ +#define XQ_RF_FRAM 0x0002 /* Framing Error */ +#define XQ_RF_MLEN 0x0004 /* Message Length Error */ + uint16 h_dest_err; /* Host Counter - Unrecognized Frame Destination Error */ + uint16 r_p_lost_i; /* Receive Packet Lost: Internal Buffer Error */ + uint16 r_p_lost_s; /* Receive Packet Lost: System Buffer Error (Unavailable or Truncated) */ + uint16 h_no_buf; /* Host Counter - User Buffer Unavailable */ + uint32 mb_xmit; /* Multicast Bytes Tramsmitted */ + uint16 reserved1; /* */ + uint16 reserved2; /* */ + uint16 babble; /* Babble Counter */ +}; +#pragma pack() + struct xq_meb { /* MEB block */ uint8 type; uint8 add_lo; @@ -128,18 +250,36 @@ struct xq_device { /*+ initialized values - DO NOT MOVE */ ETH_PCALLBACK rcallback; /* read callback routine */ ETH_PCALLBACK wcallback; /* write callback routine */ - ETH_MAC mac; /* MAC address */ + ETH_MAC mac; /* Hardware MAC address */ enum xq_type type; /* controller type */ - int poll; /* poll ethernet times/sec */ + enum xq_type mode; /* controller operating mode */ + uint16 poll; /* configured poll ethernet times/sec for receive */ + uint16 coalesce_latency; /* microseconds to hold-off interrupts when not polling */ + uint16 coalesce_latency_ticks; /* instructions in coalesce_latency microseconds */ struct xq_sanity sanity; /* sanity timer information */ /*- initialized values - DO NOT MOVE */ /* I/O register storage */ - uint16 addr[6]; + uint16 rbdl[2]; uint16 xbdl[2]; uint16 var; uint16 csr; + + uint16 srr; /* Status and Response Register - DELQA-T only */ + uint16 srqr; /* Synchronous Request Register - DELQA-T only */ + uint32 iba; /* Init Block Address Register - DELQA-T only */ + uint16 icr; /* Interrupt Request Register - DELQA-T only */ + uint16 pending_interrupt; /* Pending Interrupt - DELQA-T only */ + struct xq_turbo_init_block + init; + struct transmit_buffer_descriptor + xring[XQ_TURBO_XM_BCNT]; /* Transmit Buffer Ring */ + uint32 tbindx; /* Transmit Buffer Ring Index */ + struct receive_buffer_descriptor + rring[XQ_TURBO_RC_BCNT]; /* Receive Buffer Ring */ + uint32 rbindx; /* Receive Buffer Ring Index */ + uint32 irq; /* interrupt request flag */ /* buffers, etc. */ @@ -151,11 +291,11 @@ struct xq_device { uint32 rbdl_ba; uint32 xbdl_ba; ETH_DEV* etherface; - int receiving; ETH_PACK read_buffer; ETH_PACK write_buffer; ETH_QUE ReadQ; - int idtmr; /* countdown for ID Timer */ + int32 idtmr; /* countdown for ID Timer */ + uint32 must_poll; /* receiver must poll instead of counting on asynch polls */ }; struct xq_controller { @@ -207,6 +347,7 @@ typedef struct xq_controller CTLR; #define XQ_VEC_RO 0x5C02 /* Read-Only bits */ #define XQ_VEC_RW 0xA3FD /* Read/Write bits */ +/* DEQNA - DELQA Normal Mode Buffer Descriptors */ #define XQ_DSC_V 0x8000 /* Valid bit */ #define XQ_DSC_C 0x4000 /* Chain bit */ #define XQ_DSC_E 0x2000 /* End of Message bit [Transmit only] */ @@ -214,11 +355,38 @@ typedef struct xq_controller CTLR; #define XQ_DSC_L 0x0080 /* Low Byte Termination bit [Transmit only] */ #define XQ_DSC_H 0x0040 /* High Byte Start bit [Transmit only] */ +/* DEQNA - DELQA Normal Mode Setup Packet Flags */ #define XQ_SETUP_MC 0x0001 /* multicast bit */ #define XQ_SETUP_PM 0x0002 /* promiscuous bit */ #define XQ_SETUP_LD 0x000C /* led bits */ #define XQ_SETUP_ST 0x0070 /* sanity timer bits */ +/* DELQA-T Mode - Status and Response Register (SRR) */ +#define XQ_SRR_FES 0x8000 /* Fatal Error Summary [RO] */ +#define XQ_SRR_CHN 0x4000 /* Chaining Error [RO] */ +#define XQ_SRR_NXM 0x1000 /* Non-Existant Memory Error [RO] */ +#define XQ_SRR_PAR 0x0800 /* Parity Error (Qbus) [RO] */ +#define XQ_SRR_IME 0x0400 /* Internal Memory Error [RO] */ +#define XQ_SRR_TBL 0x0200 /* Transmit Buffer Too Long Error [RO] */ +#define XQ_SRR_RESP 0x0003 /* Synchronous Response Field [RO] */ +#define XQ_SRR_TRBO 0x0001 /* Select Turbo Response [RO] */ +#define XQ_SRR_STRT 0x0002 /* Start Device Response [RO] */ +#define XQ_SRR_STOP 0x0003 /* Stop Device Response [RO] */ + +/* DELQA-T Mode - Synchronous Request Register (SRQR) */ +#define XQ_SRQR_STRT 0x0002 /* Start Device Request [WO] */ +#define XQ_SRQR_STOP 0x0003 /* Stop Device Request [WO] */ +#define XQ_SRQR_RW 0x0003 /* Writable Bits in SRQR [WO] */ + +/* DELQA-T Mode - Asynchronous Request Register (ARQR) */ +#define XQ_ARQR_TRQ 0x8000 /* Transmit Request [WO] */ +#define XQ_ARQR_RRQ 0x0080 /* Receieve Request [WO] */ +#define XQ_ARQR_SR 0x0002 /* Software Reset Request [WO] */ + +/* DELQA-T Mode - Interrupt Control Register (ICR) */ +#define XQ_ICR_ENA 0x0001 /* Interrupt Enabled [WO] */ + + /* debugging bitmaps */ #define DBG_TRC 0x0001 /* trace routine calls */ #define DBG_REG 0x0002 /* trace read/write registers */ @@ -227,7 +395,8 @@ typedef struct xq_controller CTLR; #define DBG_WRN 0x0010 /* display warnings */ #define DBG_SAN 0x0020 /* display sanity timer info */ #define DBG_SET 0x0040 /* display setup info */ -#define DBG_PCK 0x0080 /* display packets */ +#define DBG_PCK 0x0080 /* display packet headers */ +#define DBG_DAT 0x0100 /* display packet data */ #define DBG_ETH 0x8000 /* debug ethernet device */ #endif /* _PDP11_XQ_H */ diff --git a/PDP11/pdp11_xu.c b/PDP11/pdp11_xu.c index 502a6768..c8ff52f4 100644 --- a/PDP11/pdp11_xu.c +++ b/PDP11/pdp11_xu.c @@ -1,7 +1,7 @@ /* pdp11_xu.c: DEUNA/DELUA ethernet controller simulator ------------------------------------------------------------------------------ - Copyright (c) 2003-2007, David T. Hittner + Copyright (c) 2003-2011, David T. Hittner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -30,7 +30,7 @@ Digital DELUA Users Guide, Part# EK-DELUA-UG-002 Digital DEUNA Users Guide, Part# EK-DEUNA-UG-001 These manuals can be found online at: - http://www.spies.com/~aek/pdf/dec/unibus + http://www.bitsavers.org/pdf/dec/unibus Testing performed: 1) Receives/Transmits single packet under custom RSX driver @@ -41,7 +41,13 @@ DECNET - SET HOST in/out, COPY in/out TCP/IP - PING in/out; SET HOST/TELNET in/out, COPY/FTP in/out Clustering - Successfully clustered with AlphaVMS 8.2 - 4) Runs VAX EVDWA diagnostic tests 1-10; tests 11-19 (M68000/ROM/RAM) fail + 4) VMS 4.7 on VAX780 summary: + (Jan/2011: Win7 x64 host; MS VC++ 2008; SIMH v3.8-2 rc1 base; WinPcap 4.1) + LAT - SET HOST/LAT in (no outbound exists) + DECNET - SET HOST in/out, DIR in/out, COPY in/out + TCP/IP - no kit available to test + Clustering - not tested + 5) Runs VAX EVDWA diagnostic tests 1-10; tests 11-19 (M68000/ROM/RAM) fail Known issues: 1) Most auxiliary commands are not implemented yet. @@ -56,6 +62,14 @@ Modification history: + 12-Jan-11 DTH Added SHOW XU FILTERS modifier + 11-Jan-11 DTH Corrected SELFTEST command, enabling use by VMS 3.7, VMS 4.7, and Ultrix 1.1 + 09-Dec-10 MP Added address conflict check during attach. + 06-Dec-10 MP Added loopback processing support + 30-Nov-10 MP Fixed the fact that no broadcast packets were received by the DEUNA + 15-Aug-08 MP Fixed transmitted packets to have the correct source MAC address. + Fixed incorrect address filter setting calling eth_filter(). + 23-Jan-08 MP Added debugging support to display packet headers and packet data 18-Jun-07 RMS Added UNIT_IDLE flag 03-May-07 DTH Added missing FC_RMAL command; cleared multicast on write 29-Oct-06 RMS Synced poll and clock @@ -112,6 +126,7 @@ void xu_clrint (CTLR* xu); void xu_process_receive(CTLR* xu); void xu_dump_rxring(CTLR* xu); void xu_dump_txring(CTLR* xu); +t_stat xu_show_filters (FILE* st, UNIT* uptr, int32 val, void* desc); DIB xua_dib = { IOBA_XU, IOLN_XU, &xu_rd, &xu_wr, 1, IVCL (XU), VEC_XU, {&xu_int} }; @@ -143,6 +158,8 @@ MTAB xu_mod[] = { NULL, ð_show, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATS", "STATS", &xu_set_stats, &xu_show_stats, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "FILTERS", "FILTERS", + NULL, &xu_show_filters, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE={DEUNA|DELUA}", &xu_set_type, &xu_show_type, NULL }, { 0 }, @@ -156,6 +173,7 @@ DEBTAB xu_debug[] = { {"WARN", DBG_WRN}, {"REG", DBG_REG}, {"PACKET", DBG_PCK}, + {"DATA", DBG_DAT}, {"ETH", DBG_ETH}, {0} }; @@ -294,20 +312,39 @@ t_stat xu_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc) t_stat xu_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc) { - char* fmt = " %-24s%d\n"; + char* fmt = " %-26s%d\n"; CTLR* xu = xu_unit2ctlr(uptr); struct xu_stats* stats = &xu->var->stats; fprintf(st, "Ethernet statistics:\n"); - fprintf(st, fmt, "Seconds since cleared:", stats->secs); - fprintf(st, fmt, "Recv frames:", stats->frecv); - fprintf(st, fmt, "Recv dbytes:", stats->rbytes); - fprintf(st, fmt, "Xmit frames:", stats->ftrans); - fprintf(st, fmt, "Xmit dbytes:", stats->tbytes); - fprintf(st, fmt, "Recv frames(multicast):", stats->mfrecv); - fprintf(st, fmt, "Recv dbytes(multicast):", stats->mrbytes); - fprintf(st, fmt, "Xmit frames(multicast):", stats->mftrans); - fprintf(st, fmt, "Xmit dbytes(multicast):", stats->mtbytes); + fprintf(st, fmt, "Seconds since cleared:", stats->secs); + fprintf(st, fmt, "Recv frames:", stats->frecv); + fprintf(st, fmt, "Recv dbytes:", stats->rbytes); + fprintf(st, fmt, "Xmit frames:", stats->ftrans); + fprintf(st, fmt, "Xmit dbytes:", stats->tbytes); + fprintf(st, fmt, "Recv frames(multicast):", stats->mfrecv); + fprintf(st, fmt, "Recv dbytes(multicast):", stats->mrbytes); + fprintf(st, fmt, "Xmit frames(multicast):", stats->mftrans); + fprintf(st, fmt, "Xmit dbytes(multicast):", stats->mtbytes); + fprintf(st, fmt, "Loopback forward Frames:", stats->loopf); + return SCPE_OK; +} + +t_stat xu_show_filters (FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xu = xu_unit2ctlr(uptr); + char buffer[20]; + int i; + + fprintf(st, "Filters:\n"); + for (i=0; ivar->setup.macs[i], buffer); + fprintf(st, " [%2d]: %s\n", i, buffer); + } + if (xu->var->setup.multicast) + fprintf(st, "All Multicast Receive Mode\n"); + if (xu->var->setup.promiscuous) + fprintf(st, "Promiscuous Receive Mode\n"); return SCPE_OK; } @@ -359,13 +396,63 @@ void bit_stat16(uint16* stat, uint16 bits) *stat |= bits; } +t_stat xu_process_loopback(CTLR* xu, ETH_PACK* pack) +{ + ETH_PACK response; + ETH_MAC physical_address; + t_stat status; + int offset = 16 + (pack->msg[14] | (pack->msg[15] << 8)); + int function = pack->msg[offset] | (pack->msg[offset+1] << 8); + + sim_debug(DBG_TRC, xu->dev, "xu_process_loopback()\n"); + + if (function != 2 /*forward*/) + return SCPE_NOFNC; + + /* create forward response packet */ + memcpy (&response, pack, sizeof(ETH_PACK)); + memcpy (physical_address, xu->var->setup.macs[0], sizeof(ETH_MAC)); + memcpy (&response.msg[0], &response.msg[offset+2], sizeof(ETH_MAC)); + memcpy (&response.msg[6], physical_address, sizeof(ETH_MAC)); + offset += 8; + response.msg[14] = offset & 0xFF; + response.msg[15] = (offset >> 8) & 0xFF; + + /* send response packet */ + status = eth_write(xu->var->etherface, &response, NULL); + ++xu->var->stats.loopf; + + if (DBG_PCK & xu->dev->dctrl) + eth_packet_trace_ex(xu->var->etherface, response.msg, response.len, ((function == 1) ? "xu-loopbackreply" : "xu-loopbackforward"), DBG_DAT & xu->dev->dctrl, DBG_PCK); + + return status; +} + t_stat xu_process_local (CTLR* xu, ETH_PACK* pack) { - return SCPE_NOFNC; /* not implemented yet */ + /* returns SCPE_OK if local processing occurred, + otherwise returns SCPE_NOFNC or some other code */ + int protocol; + + sim_debug(DBG_TRC, xu->dev, "xu_process_local()\n"); + + protocol = pack->msg[12] | (pack->msg[13] << 8); + switch (protocol) { + case 0x0090: /* ethernet loopback */ + return xu_process_loopback(xu, pack); + break; + case 0x0260: /* MOP remote console */ + return SCPE_NOFNC; /* not implemented yet */ + break; + } + return SCPE_NOFNC; } void xu_read_callback(CTLR* xu, int status) { + if (DBG_PCK & xu->dev->dctrl) + eth_packet_trace_ex(xu->var->etherface, xu->var->read_buffer.msg, xu->var->read_buffer.len, "xu-recvd", DBG_DAT & xu->dev->dctrl, DBG_PCK); + /* process any packets locally that can be */ status = xu_process_local (xu, &xu->var->read_buffer); @@ -442,6 +529,8 @@ t_stat xu_system_id (CTLR* xu, const ETH_MAC dest, uint16 receipt_id) /* write system id */ system_id.len = 60; status = eth_write(xu->var->etherface, &system_id, NULL); + if (DBG_PCK & xu->dev->dctrl) + eth_packet_trace_ex(xu->var->etherface, system_id.msg, system_id.len, "xu-systemid", DBG_DAT & xu->dev->dctrl, DBG_PCK); return status; } @@ -524,6 +613,7 @@ void xu_setclrint(CTLR* xu, int32 bits) t_stat xu_sw_reset (CTLR* xu) { t_stat status; + int i; sim_debug(DBG_TRC, xu->dev, "xu_sw_reset()\n"); @@ -559,10 +649,12 @@ t_stat xu_sw_reset (CTLR* xu) /* reset ethernet interface */ memcpy (xu->var->setup.macs[0], xu->var->mac, sizeof(ETH_MAC)); - xu->var->setup.mac_count = 1; + for (i=0; i<6; i++) + xu->var->setup.macs[1][i] = 0xff; /* Broadcast Address */ + xu->var->setup.mac_count = 2; if (xu->var->etherface) status = eth_filter (xu->var->etherface, xu->var->setup.mac_count, - &xu->var->mac, xu->var->setup.multicast, + xu->var->setup.macs, xu->var->setup.multicast, xu->var->setup.promiscuous); /* activate device if not disabled */ @@ -670,24 +762,24 @@ int32 xu_command(CTLR* xu) case FC_RMAL: /* read multicast address list */ mtlen = (xu->var->pcb[2] & 0xFF00) >> 8; udbb = xu->var->pcb[1] | ((xu->var->pcb[2] & 03) << 16); - wstatus = Map_WriteB(udbb, mtlen * 3, (uint8*) &xu->var->setup.macs[1]); - break; + wstatus = Map_WriteB(udbb, mtlen * 3, (uint8*) &xu->var->setup.macs[2]); + break; case FC_WMAL: /* write multicast address list */ mtlen = (xu->var->pcb[2] & 0xFF00) >> 8; -sim_debug(DBG_TRC, xu->dev, "FC_WAL: mtlen=%d\n", mtlen); + sim_debug(DBG_TRC, xu->dev, "FC_WAL: mtlen=%d\n", mtlen); if (mtlen > 10) return PCSR0_PCEI; udbb = xu->var->pcb[1] | ((xu->var->pcb[2] & 03) << 16); - /* clear existing multicast list */ - for (i=1; ivar->setup.macs[i][j] = 0; - } - /* get multicast list from host */ - rstatus = Map_ReadB(udbb, mtlen * 6, (uint8*) &xu->var->setup.macs[1]); + /* clear existing multicast list */ + for (i=2; ivar->setup.macs[i][j] = 0; + } + /* get multicast list from host */ + rstatus = Map_ReadB(udbb, mtlen * 6, (uint8*) &xu->var->setup.macs[2]); if (rstatus == 0) { - xu->var->setup.mac_count = mtlen + 1; + xu->var->setup.mac_count = mtlen + 2; status = eth_filter (xu->var->etherface, xu->var->setup.mac_count, xu->var->setup.macs, xu->var->setup.multicast, xu->var->setup.promiscuous); @@ -807,7 +899,7 @@ sim_debug(DBG_TRC, xu->dev, "FC_WAL: mtlen=%d\n", mtlen); case FC_WMODE: /* write mode register */ value = xu->var->mode; xu->var->mode = xu->var->pcb[1]; -sim_debug(DBG_TRC, xu->dev, "FC_WMODE: mode=%04x\n", xu->var->mode); + sim_debug(DBG_TRC, xu->dev, "FC_WMODE: mode=%04x\n", xu->var->mode); /* set promiscuous and multicast flags */ xu->var->setup.promiscuous = (xu->var->mode & MODE_PROM) ? 1 : 0; @@ -816,7 +908,7 @@ sim_debug(DBG_TRC, xu->dev, "FC_WMODE: mode=%04x\n", xu->var->mode); /* if promiscuous or multicast flags changed, change filter */ if ((value ^ xu->var->mode) & (MODE_PROM | MODE_ENAL)) status = eth_filter (xu->var->etherface, xu->var->setup.mac_count, - &xu->var->mac, xu->var->setup.multicast, + xu->var->setup.macs, xu->var->setup.multicast, xu->var->setup.promiscuous); break; @@ -1151,6 +1243,11 @@ void xu_process_transmit(CTLR* xu) runt = 1; } + /* As described in the DEUNA User Guide (Section 4.7), the DEUNA is responsible + for inserting the appropriate source MAC address in the outgoing packet header, + so we do that now. */ + memcpy(xu->var->write_buffer.msg+6, (uint8*)&xu->var->setup.macs[0], sizeof(ETH_MAC)); + /* are we in internal loopback mode ? */ if ((xu->var->mode & MODE_LOOP) && (xu->var->mode & MODE_INTL)) { /* just put packet in receive buffer */ @@ -1160,6 +1257,9 @@ void xu_process_transmit(CTLR* xu) wstatus = eth_write(xu->var->etherface, &xu->var->write_buffer, xu->var->wcallback); if (wstatus) xu->var->pcsr0 |= PCSR0_PCEI; + else + if (DBG_PCK & xu->dev->dctrl) + eth_packet_trace_ex(xu->var->etherface, xu->var->write_buffer.msg, xu->var->write_buffer.len, "xu-write", DBG_DAT & xu->dev->dctrl, DBG_PCK); } /* update transmit status in transmit buffer */ @@ -1263,7 +1363,14 @@ void xu_port_command (CTLR* xu) break; case CMD_SELFTEST: /* SELFTEST */ - xu_sw_reset(xu); + /* + SELFTEST is a <=15-second self diagnostic test, setting various + error flags and the DONE (DNI) flag when complete. For simulation + purposes, signal completion immediately with no errors. This + inexact behavior could be incompatible with any guest machine + diagnostics that are expecting to be able to monitor the + controller's progress through the diagnostic testing. + */ xu->var->pcsr0 |= PCSR0_DNI; break; @@ -1348,7 +1455,7 @@ t_stat xu_rd(int32 *data, int32 PA, int32 access) *data = xu->var->pcsr3; break; } - sim_debug(DBG_TRC, xu->dev, "xu_rd(), PCSR%d, data=%04x\n", reg, *data); + sim_debug(DBG_REG, xu->dev, "xu_rd(), PCSR%d, data=%04x\n", reg, *data); if (PA & 1) sim_debug(DBG_WRN, xu->dev, "xu_rd(), Unexpected Odd address access of PCSR%d\n", reg); return SCPE_OK; @@ -1375,7 +1482,7 @@ t_stat xu_wr(int32 data, int32 PA, int32 access) strcpy(desc, "Unknown"); break; } - sim_debug(DBG_TRC, xu->dev, "xu_wr(), PCSR%d, data=%08x, PA=%08x, access=%d[%s]\n", reg, data, PA, access, desc); + sim_debug(DBG_REG, xu->dev, "xu_wr(), PCSR%d, data=%08x, PA=%08x, access=%d[%s]\n", reg, data, PA, access, desc); switch (reg) { case 00: /* Clear write-one-to-clear interrupt bits */ @@ -1458,6 +1565,15 @@ t_stat xu_attach(UNIT* uptr, char* cptr) xu->var->etherface = 0; return status; } + if (SCPE_OK != eth_check_address_conflict (xu->var->etherface, &xu->var->mac)) { + char buf[32]; + + eth_mac_fmt(&xu->var->mac, buf); /* format ethernet mac address */ + printf("%s: MAC Address Conflict on LAN for address %s\n", xu->dev->name, buf); + if (sim_log) fprintf (sim_log, "%s: MAC Address Conflict on LAN for address %s\n", xu->dev->name, buf); + eth_close(xu->var->etherface); + return SCPE_NOATT; + } uptr->filename = tptr; uptr->flags |= UNIT_ATT; eth_setcrc(xu->var->etherface, 1); /* enable CRC */ diff --git a/PDP11/pdp11_xu.h b/PDP11/pdp11_xu.h index e07b5a53..83a49394 100644 --- a/PDP11/pdp11_xu.h +++ b/PDP11/pdp11_xu.h @@ -1,7 +1,7 @@ /* pdp11_xu.h: DEUNA/DELUA ethernet controller information ------------------------------------------------------------------------------ - Copyright (c) 2003-2008, David T. Hittner + Copyright (c) 2003-2005, David T. Hittner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -28,6 +28,7 @@ Modification history: + 23-Jan-08 MP Added debugging support to display packet headers and packet data 08-Dec-05 DTH Added load_server, increased UDBSIZE for system ID parameters 07-Jul-05 RMS Removed extraneous externs 05-Jan-04 DTH Added network statistics @@ -66,7 +67,7 @@ extern int32 int_req[IPL_HLVL]; #include "sim_ether.h" #define XU_QUE_MAX 500 /* message queue array */ -#define XU_FILTER_MAX 11 /* mac + 10 multicast addrs */ +#define XU_FILTER_MAX 12 /* mac + broadcast + 10 multicast addrs */ #define XU_SERVICE_INTERVAL 100 /* times per second */ #define XU_ID_TIMER_VAL 540 /* 9 min * 60 sec */ #define UDBSIZE 200 /* max size of UDB (in words) */ @@ -107,6 +108,7 @@ struct xu_stats { uint16 txccf; /* transmit collision test failure */ uint16 porterr; /* port driver errors */ uint16 bablcnt; /* babble counter */ + uint32 loopf; /* loopback frames processed */ }; struct xu_device { @@ -300,7 +302,8 @@ typedef struct xu_controller CTLR; #define DBG_TRC 0x0001 /* trace routine calls */ #define DBG_REG 0x0002 /* trace read/write registers */ #define DBG_WRN 0x0004 /* display warnings */ -#define DBG_PCK 0x0080 /* display packets */ +#define DBG_PCK 0x0080 /* display packet headers */ +#define DBG_DAT 0x0100 /* display packet data */ #define DBG_ETH 0x8000 /* debug ethernet device */ #endif /* _PDP11_XU_H */ diff --git a/sim_ether.c b/sim_ether.c index 09bd134b..feb04995 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -49,8 +49,8 @@ networking is needed - there may be known workarounds. Define one of the two macros below to enable networking: - USE_NETWORK - Create statically linked network code - USE_SHARED - Create dynamically linked network code (_WIN32 only) + USE_NETWORK - Create statically linked network code + USE_SHARED - Create dynamically linked network code (_WIN32 only) ------------------------------------------------------------------------------ @@ -118,13 +118,26 @@ Using the threaded approach may require special compile and/or link time switches (i.e. -lpthread or -pthread, etc.) Consult the documentation for your platform as - needed. - MUST_DO_SELECT - Specifies that when USE_READER_THREAD is active, that + needed. Although this may be 'less efficient' than the + non-threaded approach, the efficiency is an overall system + efficiency not necessarily a simulator efficiency. This + means that work is removed from the thread executing + simulated instructions so the simulated system will most + likely run faster (given that modern host CPUs are + multi-core and have someplace to do this work in parallel). + MUST_DO_SELECT - Specifies that, when USE_READER_THREAD is active, select() should be used to determin when available packets are ready for reading. Otherwise, we depend on the libpcap/kernel packet timeout specified on pcap_open_live. If USE_READER_THREAD is not set, then MUST_DO_SELECT is irrelevant + USE_TAP_NETWORK - Specifies that support for tap networking should be + included. This can be leveraged, along with OS bridging + capabilities to share a single LAN interface. This + allows device names of the form tap:tap0 to be specified + at open time. This functionality is only useful/needed + on *nix platforms since native sharing of Windows NIC + devices works with no external magic. NEED_PCAP_SENDPACKET - Specifies that you are using an older version of libpcap @@ -138,6 +151,89 @@ Modification history: + 09-Jan-11 MP Fixed missing crc data when USE_READER_THREAD is defined and + crc's are needed (only the pdp11_xu) + 16-Dec-10 MP added priority boost for read and write threads when + USE_READER_THREAD does I/O in separate threads. This helps + throughput since it allows these I/O bound threads to preempt + the main thread (which is executing simulated instructions). + 09-Dec-10 MP allowed more flexible parsing of MAC address strings + 09-Dec-10 MP Added support to determine if network address conflicts exist + 07-Dec-10 MP Reworked DECnet self detection to the more general approach + of loopback self when a Physical Address is being set. + 04-Dec-10 MP Changed eth_write to do nonblocking writes when + USE_READER_THREAD is defined. + 20-Aug-10 TVO Fix for Mac OSX 10.6 + 17-Jun-10 MP Fixed bug in the AUTODIN II hash filtering. + 14-Jun-10 MP Added support for integrated Tap networking interfaces on BSD + platforms. + 13-Jun-10 MP Added support for integrated Tap networking interfaces on Linux + platforms. + 31-May-10 MP Added support for more TOE (TCP Offload Engine) features for IPv4 + network traffic from the host and/or from hosts on the LAN. These + new TOE features are: LSO (Large Send Offload) and Jumbo packet + fragmentation support. These features allow a simulated network + device to suuport traffic when a host leverages a NIC's Large + Send Offload capabilities to fregment and/or segment outgoing + network traffic. Additionally a simulated network device can + reasonably exist on a LAN which is configured to use Jumbo frames. + 21-May-10 MP Added functionslity to fixup IP header checksums to accomodate + packets from a host with a NIC which has TOE (TCP Offload Engine) + enabled which is expected to implement the checksum computations + in hardware. Since we catch packets before they arrive at the + NIC the expected checksum insertions haven't been performed yet. + This processing is only done for packets sent from the hoat to + the guest we're supporting. In general this will be a relatively + small number of packets so it is done for all IP frame packets + coming from the hoat to the guest. In order to make the + determination of packets specifically arriving from the host we + need to know the hardware MAC address of the host NIC. Currently + determining a NIC's MAC address is relatively easy on Windows. + The non-windows code works on linux and may work on other *nix + platforms either as is or with slight modifications. The code, + as implemented, only messes with this activity if the host + interface MAC address can be determined. + 20-May-10 MP Added general support to deal with receiving packets smaller + than ETH_MIN_PACKET in length. These come from packets + looped back by some bridging mechanism and need to be padded + to the minimum frame size. A real NIC won't pass us any + packets like that. This fix belongs here since this layer + is responsible for interfacing to they physical layer + devices, AND it belongs here to get CRC processing right. + 05-Mar-08 MP Added optional multicast filtering support for doing + LANCE style AUTODIN II based hashed filtering. + 07-Feb-08 MP Added eth_show_dev to display ethernet state + Changed the return value from eth_read to return whether + or not a packet was read. No existing callers used or + checked constant return value that previously was being + supplied. + 29-Jan-08 MP Added eth_set_async to provide a mechanism (when + USE_READER_THREAD is enabled) to allow packet reception + to dynamically update the simulator event queue and + potentially avoid polling for I/O. This provides a minimal + overhead (no polling) maximal responsiveness for network + activities. + 29-Jan-08 MP Properly sequenced activities in eth_close to avoid a race + condition when USE_READER_THREAD is enabled. + 25-Jan-08 MP Changed the following when USE_READER_THREAD is enabled: + - Fixed bug when the simulated device doesn't need crc + in packet data which is read. + - Added call to pcap_setmintocopy to minimize packet + delivery latencies. + - Added ethq_destroy and used it to avoid a memory leak in + eth_close. + - Properly cleaned up pthread mutexes in eth_close. + Migrated to using sim_os_ms_sleep for a delay instead of + a call to select(). + Fixed the bpf filter used when no traffic is to be matched. + Reworked eth_add_packet_crc32 implementation to avoid an + extra buffer copy while reading packets. + Fixedup #ifdef's relating to USE_SHARED so that setting + USE_SHARED or USE_NETWORK will build a working network + environment. + 23-Jan-08 MP Reworked eth_packet_trace and eth_packet_trace_ex to allow + only output ethernet header+crc and provide a mechanism for + the simulated device to display full packet data debugging. 17-May-07 DTH Fixed non-ethernet device removal loop (from Naoki Hamada) 15-May-07 DTH Added dynamic loading of wpcap.dll; Corrected exceed max index bug in ethX lookup @@ -252,36 +348,23 @@ extern FILE *sim_log; t_stat eth_mac_scan (ETH_MAC* mac, char* strmac) { - int i, j; - short unsigned int num; - char cptr[18]; - int len = strlen(strmac); + int a0, a1, a2, a3, a4, a5; const ETH_MAC zeros = {0,0,0,0,0,0}; const ETH_MAC ones = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; ETH_MAC newmac; - /* format of string must be 6 double-digit hex bytes with valid separators - ideally, this mac scanner could allow more flexible formatting later */ - if (len != 17) return SCPE_ARG; - - /* copy string to local storage for mangling */ - strcpy(cptr, strmac); - - /* make sure byte separators are OK */ - for (i=2; i 0xFF) || (a1 > 0xFF) || (a2 > 0xFF) || (a3 > 0xFF) || (a4 > 0xFF) || (a5 > 0xFF)) + return SCPE_ARG; + newmac[0] = a0; + newmac[1] = a1; + newmac[2] = a2; + newmac[3] = a3; + newmac[4] = a4; + newmac[5] = a5; /* final check - mac cannot be broadcast or multicast address */ if (!memcmp(newmac, zeros, sizeof(ETH_MAC)) || /* broadcast */ @@ -354,22 +437,48 @@ uint32 eth_crc32(uint32 crc, const void* vbuf, size_t len) const unsigned char* buf = (const unsigned char*)vbuf; crc ^= mask; + while (len > 8) { + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + len -= 8; + } while (0 != len--) crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; return(crc ^ mask); } -void eth_add_crc32(ETH_PACK* packet) +int eth_get_packet_crc32_data(const uint8 *msg, int len, uint8 *crcdata) { - if (packet->len <= ETH_MAX_PACKET) { - uint32 crc = eth_crc32(0, packet->msg, packet->len); /* calculate CRC */ + int crc_len; + + if (len <= ETH_MAX_PACKET) { + uint32 crc = eth_crc32(0, msg, len); /* calculate CRC */ uint32 ncrc = htonl(crc); /* CRC in network order */ int size = sizeof(ncrc); /* size of crc field */ - memcpy(&packet->msg[packet->len], &ncrc, size); /* append crc to packet */ - packet->crc_len = packet->len + size; /* set packet crc length */ + memcpy(crcdata, &ncrc, size); /* append crc to packet */ + crc_len = len + size; /* set packet crc length */ } else { - packet->crc_len = 0; /* appending crc would destroy packet */ + crc_len = 0; /* appending crc would destroy packet */ } + return crc_len; +} + +int eth_add_packet_crc32(uint8 *msg, int len) +{ + int crc_len; + + if (len <= ETH_MAX_PACKET) { + crc_len = eth_get_packet_crc32_data(msg, len, &msg[len]);/* append crc to packet */ + } else { + crc_len = 0; /* appending crc would destroy packet */ + } + return crc_len; } void eth_setcrc(ETH_DEV* dev, int need_crc) @@ -377,18 +486,18 @@ void eth_setcrc(ETH_DEV* dev, int need_crc) dev->need_crc = need_crc; } -void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, char* txt, int dmp) +void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, char* txt, int detail, uint32 reason) { - if (dev->dptr->dctrl & dev->dbit) { + if (dev->dptr->dctrl & reason) { char src[20]; char dst[20]; unsigned short* proto = (unsigned short*) &msg[12]; uint32 crc = eth_crc32(0, msg, len); eth_mac_fmt((ETH_MAC*)&msg[0], dst); eth_mac_fmt((ETH_MAC*)&msg[6], src); - sim_debug(dev->dbit, dev->dptr, "%s dst: %s src: %s proto: 0x%04X len: %d crc: %X\n", + sim_debug(reason, dev->dptr, "%s dst: %s src: %s proto: 0x%04X len: %d crc: %X\n", txt, dst, src, ntohs(*proto), len, crc); - if (dmp) { + if (detail) { int i, same, group, sidx, oidx; char outbuf[80], strbuf[18]; static char hex[] = "0123456789ABCDEF"; @@ -398,7 +507,7 @@ void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, char* txt, int continue; } if (same > 0) { - sim_debug(dev->dbit, dev->dptr, "%04X thru %04X same as above\r\n", i-(16*same), i-1); + sim_debug(reason, dev->dptr, "%04X thru %04X same as above\n", i-(16*same), i-1); same = 0; } group = (((len - i) > 16) ? 16 : (len - i)); @@ -413,17 +522,17 @@ void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, char* txt, int } outbuf[oidx] = '\0'; strbuf[sidx] = '\0'; - sim_debug(dev->dbit, dev->dptr, "%04X%-48s %s\r\n", i, outbuf, strbuf); + sim_debug(reason, dev->dptr, "%04X%-48s %s\n", i, outbuf, strbuf); } if (same > 0) - sim_debug(dev->dbit, dev->dptr, "%04X thru %04X same as above\r\n", i-(16*same), len-1); + sim_debug(reason, dev->dptr, "%04X thru %04X same as above\n", i-(16*same), len-1); } } } void eth_packet_trace(ETH_DEV* dev, const uint8 *msg, int len, char* txt) { - eth_packet_trace_ex(dev, msg, len, txt, 1/*len > ETH_MAX_PACKET*/); + eth_packet_trace_ex(dev, msg, len, txt, 0, dev->dbit); } char* eth_getname(int number, char* name) @@ -441,11 +550,11 @@ char* eth_getname_bydesc(char* desc, char* name) ETH_LIST list[ETH_MAX_DEVICE]; int count = eth_devices(ETH_MAX_DEVICE, list); int i; - int j=strlen(desc); + size_t j=strlen(desc); for (i=0; i min) min = len; for (i=0; iitem) { - size_t size = sizeof(struct eth_item) * max; - que->max = max; - que->item = (struct eth_item *) malloc(size); - if (que->item) { - /* init dynamic memory */ - memset(que->item, 0, size); - } else { + que->item = (struct eth_item *) calloc(max, sizeof(struct eth_item)); + if (!que->item) { /* failed to allocate memory */ char* msg = "EthQ: failed to allocate dynamic queue[%d]\r\n"; printf(msg, max); if (sim_log) fprintf(sim_log, msg, max); return SCPE_MEM; }; + que->max = max; + }; + ethq_clear(que); + return SCPE_OK; +} + +t_stat ethq_destroy(ETH_QUE* que) +{ + /* release dynamic queue if it exists */ + ethq_clear(que); + que->max = 0; + if (que->item) { + free(que->item); + que->item = NULL; }; return SCPE_OK; } @@ -557,7 +677,7 @@ void ethq_clear(ETH_QUE* que) /* clear packet array */ memset(que->item, 0, sizeof(struct eth_item) * que->max); /* clear rest of structure */ - que->count = que->head = que->tail = que->loss = que->high = 0; + que->count = que->head = que->tail = 0; } void ethq_remove(ETH_QUE* que) @@ -572,7 +692,7 @@ void ethq_remove(ETH_QUE* que) } } -void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status) +void ethq_insert_data(ETH_QUE* que, int32 type, const uint8 *data, int used, int len, int crc_len, const uint8 *crc_data, int32 status) { struct eth_item* item; @@ -598,39 +718,58 @@ void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status) /* set information in (new) tail item */ item = &que->item[que->tail]; item->type = type; - item->packet.len = pack->len; - item->packet.used = 0; - item->packet.crc_len = pack->crc_len; - memcpy(item->packet.msg, pack->msg, ((pack->len > pack->crc_len) ? pack->len : pack->crc_len)); + item->packet.len = len; + item->packet.used = used; + item->packet.crc_len = crc_len; + memcpy(item->packet.msg, data, ((len > crc_len) ? len : crc_len)); + if (crc_data && (crc_len > len)) + memcpy(&item->packet.msg[len], crc_data, ETH_CRC_SIZE); item->packet.status = status; } +void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status) +{ + ethq_insert_data(que, type, pack->msg, pack->used, pack->len, pack->crc_len, NULL, status); +} + /*============================================================================*/ /* Non-implemented versions */ /*============================================================================*/ -#if !defined (USE_NETWORK) && !defined(USE_SHARED) +#if !defined (USE_NETWORK) && (!defined (_WIN32) || !defined (USE_SHARED)) t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) {return SCPE_NOFNC;} t_stat eth_close (ETH_DEV* dev) {return SCPE_NOFNC;} +t_stat eth_check_address_conflict (ETH_DEV* dev, + ETH_MAC* const mac) + {return SCPE_NOFNC;} +t_stat eth_set_async (ETH_DEV *dev, int latency) + {return SCPE_NOFNC;} +t_stat eth_clr_async (ETH_DEV *dev) + {return SCPE_NOFNC;} t_stat eth_write (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) {return SCPE_NOFNC;} -t_stat eth_read (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) +int eth_read (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) {return SCPE_NOFNC;} -t_stat eth_filter (ETH_DEV* dev, int addr_count, ETH_MAC* addresses, +t_stat eth_filter (ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous) {return SCPE_NOFNC;} +t_stat eth_filter_hash (ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, + ETH_BOOL all_multicast, ETH_BOOL promiscuous, ETH_MULTIHASH* const hash) + {return SCPE_NOFNC;} int eth_devices (int max, ETH_LIST* dev) {return -1;} -#else /* endif unimplemented */ +void eth_show_dev (FILE* st, ETH_DEV* dev) + {} +#else /* endif unimplemented */ /*============================================================================*/ /* WIN32, Linux, and xBSD routines use WinPcap and libpcap packages */ /* OpenVMS Alpha uses a WinPcap port and an associated execlet */ /*============================================================================*/ -#if defined (xBSD) && !defined(__APPLE__) +#if defined (xBSD) || defined(__APPLE__) #include #include #endif /* xBSD */ @@ -638,6 +777,20 @@ int eth_devices (int max, ETH_LIST* dev) #include #include +#ifdef USE_TAP_NETWORK +#if defined(__linux) +#include +#include +#include +#elif defined(USE_BSDTUNTAP) +#include +#include +#include +#else /* We don't know how to do this on the current platform */ +#undef USE_TAP_NETWORK +#endif +#endif /* USE_TAP_NETWORK */ + /* Allows windows to look up user-defined adapter names */ #if defined(_WIN32) #include @@ -648,8 +801,8 @@ int eth_devices (int max, ETH_LIST* dev) Etherial/WireShark capture_pcap.c */ /* Dynamic DLL load variables */ -static HINSTANCE hDll = 0; /* handle to DLL */ -static int dll_loaded = 0; /* 0=not loaded, 1=loaded, 2=DLL load failed, 3=Func load failed */ +static HINSTANCE hDll = 0; /* handle to DLL */ +static int dll_loaded = 0; /* 0=not loaded, 1=loaded, 2=DLL load failed, 3=Func load failed */ static char* no_wpcap = "wpcap load failure"; /* define pointers to pcap functions needed */ @@ -663,169 +816,191 @@ static void (*p_pcap_freecode) (struct bpf_program *); static char* (*p_pcap_geterr) (pcap_t *); static int (*p_pcap_lookupnet) (const char *, bpf_u_int32 *, bpf_u_int32 *, char *); static pcap_t* (*p_pcap_open_live) (const char *, int, int, int, char *); +static int (*p_pcap_setmintocopy) (pcap_t* handle, int); +static HANDLE (*p_pcap_getevent) (pcap_t *); static int (*p_pcap_sendpacket) (pcap_t* handle, const u_char* msg, int len); static int (*p_pcap_setfilter) (pcap_t *, struct bpf_program *); static char* (*p_pcap_lib_version) (void); /* load function pointer from DLL */ void load_function(char* function, void** func_ptr) { - *func_ptr = GetProcAddress(hDll, function); - if (*func_ptr == 0) { - char* msg = "Eth: Failed to find function '%s' in wpcap.dll\r\n"; - printf (msg, function); - if (sim_log) fprintf (sim_log, msg, function); - dll_loaded = 3; - } + *func_ptr = GetProcAddress(hDll, function); + if (*func_ptr == 0) { + char* msg = "Eth: Failed to find function '%s' in wpcap.dll\r\n"; + + printf (msg, function); + if (sim_log) fprintf (sim_log, msg, function); + dll_loaded = 3; + } } /* load wpcap.dll as required */ int load_wpcap(void) { - switch(dll_loaded) { - case 0: /* not loaded */ + switch(dll_loaded) { + case 0: /* not loaded */ /* attempt to load DLL */ - hDll = LoadLibrary(TEXT("wpcap.dll")); - if (hDll == 0) { - /* failed to load DLL */ - char* msg = "Eth: Failed to load wpcap.dll\r\n"; - char* msg2 = "Eth: You must install WinPcap 4.x to use networking\r\n"; - printf (msg); - printf (msg2); - if (sim_log) { - fprintf (sim_log, msg); - fprintf (sim_log, msg2); - } - dll_loaded = 2; - break; - } else { - /* DLL loaded OK */ - dll_loaded = 1; - } + hDll = LoadLibrary(TEXT("wpcap.dll")); + if (hDll == 0) { + /* failed to load DLL */ + char* msg = "Eth: Failed to load wpcap.dll\r\n"; + char* msg2 = "Eth: You must install WinPcap 4.x to use networking\r\n"; - /* load required functions; sets dll_load=3 on error */ - load_function("pcap_close", (void**) &p_pcap_close); - load_function("pcap_compile", (void**) &p_pcap_compile); - load_function("pcap_datalink", (void**) &p_pcap_datalink); - load_function("pcap_dispatch", (void**) &p_pcap_dispatch); - load_function("pcap_findalldevs", (void**) &p_pcap_findalldevs); - load_function("pcap_freealldevs", (void**) &p_pcap_freealldevs); - load_function("pcap_freecode", (void**) &p_pcap_freecode); - load_function("pcap_geterr", (void**) &p_pcap_geterr); - load_function("pcap_lookupnet", (void**) &p_pcap_lookupnet); - load_function("pcap_open_live", (void**) &p_pcap_open_live); - load_function("pcap_sendpacket", (void**) &p_pcap_sendpacket); - load_function("pcap_setfilter", (void**) &p_pcap_setfilter); - load_function("pcap_lib_version", (void**) &p_pcap_lib_version); + printf (msg); + printf (msg2); + if (sim_log) { + fprintf (sim_log, msg); + fprintf (sim_log, msg2); + } + dll_loaded = 2; + break; + } else { + /* DLL loaded OK */ + dll_loaded = 1; + } - if (dll_loaded == 1) { - /* log successful load */ - char* version = p_pcap_lib_version(); - printf("%s\n", version); - if (sim_log) - fprintf(sim_log, "%s\n", version); - } - break; - default: /* loaded or failed */ - break; - } - return (dll_loaded == 1) ? 1 : 0; + /* load required functions; sets dll_load=3 on error */ + load_function("pcap_close", (void**) &p_pcap_close); + load_function("pcap_compile", (void**) &p_pcap_compile); + load_function("pcap_datalink", (void**) &p_pcap_datalink); + load_function("pcap_dispatch", (void**) &p_pcap_dispatch); + load_function("pcap_findalldevs", (void**) &p_pcap_findalldevs); + load_function("pcap_freealldevs", (void**) &p_pcap_freealldevs); + load_function("pcap_freecode", (void**) &p_pcap_freecode); + load_function("pcap_geterr", (void**) &p_pcap_geterr); + load_function("pcap_lookupnet", (void**) &p_pcap_lookupnet); + load_function("pcap_open_live", (void**) &p_pcap_open_live); + load_function("pcap_setmintocopy", (void**) &p_pcap_setmintocopy); + load_function("pcap_getevent", (void**) &p_pcap_getevent); + load_function("pcap_sendpacket", (void**) &p_pcap_sendpacket); + load_function("pcap_setfilter", (void**) &p_pcap_setfilter); + load_function("pcap_lib_version", (void**) &p_pcap_lib_version); + + if (dll_loaded == 1) { + /* log successful load */ + char* version = p_pcap_lib_version(); + printf("%s\n", version); + if (sim_log) + fprintf(sim_log, "%s\n", version); + } + break; + default: /* loaded or failed */ + break; + } + return (dll_loaded == 1) ? 1 : 0; } /* define functions with dynamic revectoring */ void pcap_close(pcap_t* a) { - if (load_wpcap() != 0) { - p_pcap_close(a); - } + if (load_wpcap() != 0) { + p_pcap_close(a); + } } int pcap_compile(pcap_t* a, struct bpf_program* b, char* c, int d, bpf_u_int32 e) { - if (load_wpcap() != 0) { - return p_pcap_compile(a, b, c, d, e); - } else { - return 0; - } + if (load_wpcap() != 0) { + return p_pcap_compile(a, b, c, d, e); + } else { + return 0; + } } int pcap_datalink(pcap_t* a) { - if (load_wpcap() != 0) { - return p_pcap_datalink(a); - } else { - return 0; - } + if (load_wpcap() != 0) { + return p_pcap_datalink(a); + } else { + return 0; + } } int pcap_dispatch(pcap_t* a, int b, pcap_handler c, u_char* d) { - if (load_wpcap() != 0) { - return p_pcap_dispatch(a, b, c, d); - } else { - return 0; - } + if (load_wpcap() != 0) { + return p_pcap_dispatch(a, b, c, d); + } else { + return 0; + } } int pcap_findalldevs(pcap_if_t** a, char* b) { - if (load_wpcap() != 0) { - return p_pcap_findalldevs(a, b); - } else { - *a = 0; - strcpy(b, no_wpcap); - return -1; - } + if (load_wpcap() != 0) { + return p_pcap_findalldevs(a, b); + } else { + *a = 0; + strcpy(b, no_wpcap); + return -1; + } } void pcap_freealldevs(pcap_if_t* a) { - if (load_wpcap() != 0) { - p_pcap_freealldevs(a); - } + if (load_wpcap() != 0) { + p_pcap_freealldevs(a); + } } void pcap_freecode(struct bpf_program* a) { - if (load_wpcap() != 0) { - p_pcap_freecode(a); - } + if (load_wpcap() != 0) { + p_pcap_freecode(a); + } } char* pcap_geterr(pcap_t* a) { - if (load_wpcap() != 0) { - return p_pcap_geterr(a); - } else { - return (char*) 0; - } + if (load_wpcap() != 0) { + return p_pcap_geterr(a); + } else { + return (char*) 0; + } } int pcap_lookupnet(const char* a, bpf_u_int32* b, bpf_u_int32* c, char* d) { - if (load_wpcap() != 0) { - return p_pcap_lookupnet(a, b, c, d); - } else { - return 0; - } + if (load_wpcap() != 0) { + return p_pcap_lookupnet(a, b, c, d); + } else { + return 0; + } } pcap_t* pcap_open_live(const char* a, int b, int c, int d, char* e) { - if (load_wpcap() != 0) { - return p_pcap_open_live(a, b, c, d, e); - } else { - return (pcap_t*) 0; - } + if (load_wpcap() != 0) { + return p_pcap_open_live(a, b, c, d, e); + } else { + return (pcap_t*) 0; + } +} + +int pcap_setmintocopy(pcap_t* a, int b) { + if (load_wpcap() != 0) { + return p_pcap_setmintocopy(a, b); + } else { + return 0; + } +} + +HANDLE pcap_getevent(pcap_t* a) { + if (load_wpcap() != 0) { + return p_pcap_getevent(a); + } else { + return (HANDLE) 0; + } } int pcap_sendpacket(pcap_t* a, const u_char* b, int c) { - if (load_wpcap() != 0) { - return p_pcap_sendpacket(a, b, c); - } else { - return 0; - } + if (load_wpcap() != 0) { + return p_pcap_sendpacket(a, b, c); + } else { + return 0; + } } int pcap_setfilter(pcap_t* a, struct bpf_program* b) { - if (load_wpcap() != 0) { - return p_pcap_setfilter(a, b); - } else { - return 0; - } + if (load_wpcap() != 0) { + return p_pcap_setfilter(a, b); + } else { + return 0; + } } #endif /* Some platforms have always had pcap_sendpacket */ -#if defined(_WIN32) || defined(VMS) +#if defined(_WIN32) || defined(__VMS) #define HAS_PCAP_SENDPACKET 1 #else /* The latest libpcap and WinPcap all have pcap_sendpacket */ @@ -848,56 +1023,297 @@ int pcap_sendpacket(pcap_t* handle, const u_char* msg, int len) } #endif /* !HAS_PCAP_SENDPACKET */ +#ifdef _WIN32 +#include +#include + +static int pcap_mac_if_win32(char *AdapterName, UCHAR MACAddress[6]) +{ + LPADAPTER lpAdapter; + PPACKET_OID_DATA OidData; + BOOLEAN Status; + int ReturnValue; + HINSTANCE hDll; /* handle to DLL */ + LPADAPTER (*p_PacketOpenAdapter)(PCHAR AdapterName); + VOID (*p_PacketCloseAdapter)(LPADAPTER lpAdapter); + BOOLEAN (*p_PacketRequest)(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData); + + hDll = LoadLibrary(TEXT("packet.dll")); + p_PacketOpenAdapter = (void *)GetProcAddress(hDll, "PacketOpenAdapter"); + p_PacketCloseAdapter = (void *)GetProcAddress(hDll, "PacketCloseAdapter"); + p_PacketRequest = (void *)GetProcAddress(hDll, "PacketRequest"); + + /* Open the selected adapter */ + + lpAdapter = p_PacketOpenAdapter(AdapterName); + + if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE)) { + FreeLibrary(hDll); + return -1; + } + + /* Allocate a buffer to get the MAC adress */ + + OidData = malloc(6 + sizeof(PACKET_OID_DATA)); + if (OidData == NULL) { + p_PacketCloseAdapter(lpAdapter); + FreeLibrary(hDll); + return -1; + } + + /* Retrieve the adapter MAC querying the NIC driver */ + + OidData->Oid = OID_802_3_CURRENT_ADDRESS; + + OidData->Length = 6; + memset(OidData->Data, 0, 6); + + Status = p_PacketRequest(lpAdapter, FALSE, OidData); + if(Status) { + memcpy(MACAddress, OidData->Data, 6); + ReturnValue = 0; + } else + ReturnValue = -1; + + free(OidData); + p_PacketCloseAdapter(lpAdapter); + FreeLibrary(hDll); + return ReturnValue; +} +#endif + +static void eth_get_nic_hw_addr(ETH_DEV* dev, char *devname) +{ + memset(&dev->host_nic_phy_hw_addr, 0, sizeof(dev->host_nic_phy_hw_addr)); + dev->have_host_nic_phy_addr = 0; +#ifdef _WIN32 + if (!pcap_mac_if_win32(devname, (UCHAR *)&dev->host_nic_phy_hw_addr)) + dev->have_host_nic_phy_addr = 1; +#elif !defined (__VMS) +{ + char command[1024]; + FILE *f; + + memset(command, 0, sizeof(command)); + snprintf(command, sizeof(command)-1, "ifconfig %s | grep [0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F] >NIC.hwaddr", devname); + system(command); + if (f = fopen("NIC.hwaddr", "r")) { + if (fgets(command, sizeof(command)-1, f)) { + char *p1, *p2; + + p1 = strchr(command, ':'); + while (p1) { + p2 = strchr(p1+1, ':'); + if (p2 == p1+3) { + int mac_bytes[6]; + if (6 == sscanf(p1-2, "%02x:%02x:%02x:%02x:%02x:%02x", &mac_bytes[0], &mac_bytes[1], &mac_bytes[2], &mac_bytes[3], &mac_bytes[4], &mac_bytes[5])) { + dev->host_nic_phy_hw_addr[0] = mac_bytes[0]; + dev->host_nic_phy_hw_addr[1] = mac_bytes[1]; + dev->host_nic_phy_hw_addr[2] = mac_bytes[2]; + dev->host_nic_phy_hw_addr[3] = mac_bytes[3]; + dev->host_nic_phy_hw_addr[4] = mac_bytes[4]; + dev->host_nic_phy_hw_addr[5] = mac_bytes[5]; + dev->have_host_nic_phy_addr = 1; + } + break; + } + p1 = p2; + } + } + fclose(f); + remove("NIC.hwaddr"); + } +} +#endif +} + +/* Forward declarations */ +static void +_eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data); + +static t_stat +_eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine); + #if defined (USE_READER_THREAD) #include -void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data); - static void * _eth_reader(void *arg) { ETH_DEV* volatile dev = (ETH_DEV*)arg; int status; +int sched_policy; +struct sched_param sched_priority; +#if defined (_WIN32) +HANDLE hWait = pcap_getevent ((pcap_t*)dev->handle); +#endif +#if defined (MUST_DO_SELECT) struct timeval timeout; +int sel_ret; +int pcap_fd; - timeout.tv_sec = 0; - timeout.tv_usec = 200*1000; +if (dev->pcap_mode) + pcap_fd = pcap_get_selectable_fd((pcap_t *)dev->handle); +else + pcap_fd = dev->fd_handle; +#endif sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n"); - while (dev->handle) { -#if defined (MUST_DO_SELECT) - int sel_ret; + /* 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); + while ((dev->pcap_mode && dev->handle) || dev->fd_handle) { +#if defined (MUST_DO_SELECT) fd_set setl; FD_ZERO(&setl); - FD_SET(pcap_get_selectable_fd((pcap_t *)dev->handle), &setl); - sel_ret = select(1+pcap_get_selectable_fd((pcap_t *)dev->handle), &setl, NULL, NULL, &timeout); + FD_SET(pcap_fd, &setl); + timeout.tv_sec = 0; + timeout.tv_usec = 250*1000; + sel_ret = select(1+pcap_fd, &setl, NULL, NULL, &timeout); if (sel_ret < 0 && errno != EINTR) break; if (sel_ret > 0) { - /* dispatch read request queue available packets */ - status = pcap_dispatch((pcap_t*)dev->handle, -1, ð_callback, (u_char*)dev); - } +#else /* !MUST_DO_SELECT */ +#if defined (_WIN32) + if (WAIT_OBJECT_0 == WaitForSingleObject (hWait, 250)) { #else - /* dispatch read request queue available packets */ - status = pcap_dispatch((pcap_t*)dev->handle, 1, ð_callback, (u_char*)dev); + if (1) { #endif +#endif /* MUST_DO_SELECT */ + if ((dev->pcap_mode) && (!dev->handle)) + break; + if ((!dev->pcap_mode) && (!dev->fd_handle)) + break; + /* dispatch read request queue available packets */ + if (dev->pcap_mode) + status = pcap_dispatch ((pcap_t*)dev->handle, -1, &_eth_callback, (u_char*)dev); +#ifdef USE_TAP_NETWORK + else { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; + + memset(&header, 0, sizeof(header)); + len = read(dev->fd_handle, buf, sizeof(buf)); + if (len > 0) { + status = 1; + header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } else { + status = 0; + } + } +#endif /* USE_TAP_NETWORK */ + + if ((status > 0) && (dev->asynch_io)) { + int wakeup_needed; + pthread_mutex_lock (&dev->lock); + wakeup_needed = (dev->read_queue.count != 0); + pthread_mutex_unlock (&dev->lock); + if (wakeup_needed) { + sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); + sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); + } + } + } } sim_debug(dev->dbit, dev->dptr, "Reader Thread Exiting\n"); return NULL; } + +static void * +_eth_writer(void *arg) +{ +ETH_DEV* volatile dev = (ETH_DEV*)arg; +struct write_request *request; +int sched_policy; +struct sched_param sched_priority; + + /* 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(dev->dbit, dev->dptr, "Writer Thread Starting\n"); + + pthread_mutex_lock (&dev->writer_lock); + while ((dev->pcap_mode && dev->handle) || dev->fd_handle) { + pthread_cond_wait (&dev->writer_cond, &dev->writer_lock); + while (request = dev->write_requests) { + /* Pull buffer off request list */ + dev->write_requests = request->next; + pthread_mutex_unlock (&dev->writer_lock); + + dev->write_status = _eth_write(dev, &request->packet, NULL); + + pthread_mutex_lock (&dev->writer_lock); + /* Put buffer on free buffer list */ + request->next = dev->write_buffers; + dev->write_buffers = request; + } + } + pthread_mutex_unlock (&dev->writer_lock); + + sim_debug(dev->dbit, dev->dptr, "Writer Thread Exiting\n"); + return NULL; +} #endif +t_stat eth_set_async (ETH_DEV *dev, int latency) +{ +#if !defined(USE_READER_THREAD) || !defined(SIM_ASYNCH_IO) + char *msg = "Eth: can't operate asynchronously, must poll\r\n"; + printf ("%s", msg); + if (sim_log) fprintf (sim_log, "%s", msg); + return SCPE_NOFNC; +#else + int wakeup_needed; + + dev->asynch_io = 1; + dev->asynch_io_latency = latency; + pthread_mutex_lock (&dev->lock); + wakeup_needed = (dev->read_queue.count != 0); + pthread_mutex_unlock (&dev->lock); + if (wakeup_needed) { + sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); + sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); + } +#endif + return SCPE_OK; +} + +t_stat eth_clr_async (ETH_DEV *dev) +{ +#if !defined(USE_READER_THREAD) || !defined(SIM_ASYNCH_IO) + return SCPE_NOFNC; +#else + /* make sure device exists */ + if (!dev) return SCPE_UNATT; + + dev->asynch_io = 0; + return SCPE_OK; +#endif +} + t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) { - const int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ; + int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ; char errbuf[PCAP_ERRBUF_SIZE]; char temp[1024]; char* savname = name; int num; char* msg; + if (bufsz < ETH_MAX_JUMBO_FRAME) + bufsz = ETH_MAX_JUMBO_FRAME; /* Enable handling of jumbo frames */ + /* initialize device */ eth_zero(dev); @@ -919,23 +1335,87 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) /* probably is not ethX and has no description */ savname = eth_getname_byname(name, temp); if (savname == 0) /* didn't translate */ - return SCPE_OPENERR; + savname = name; } } /* attempt to connect device */ memset(errbuf, 0, sizeof(errbuf)); - dev->handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf); - if (!dev->handle) { /* can't open device */ - msg = "Eth: pcap_open_live error - %s\r\n"; + if (strncmp("tap:", savname, 4)) { + dev->handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf); + if (!dev->handle) { /* can't open device */ + msg = "Eth: pcap_open_live error - %s\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } + dev->pcap_mode = 1; + } else { + int tun = -1; /* TUN/TAP Socket */ + int on = 1; + char dev_name[64] = ""; + +#if defined(USE_TAP_NETWORK) + if (!strcmp(savname, "tap:tapN")) { + msg = "Eth: Must specify actual tap device name (i.e. tap:tap0)\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } +#endif +#if defined(__linux) && defined(USE_TAP_NETWORK) + if ((tun = open("/dev/net/tun", O_RDWR)) >= 0) { + struct ifreq ifr; /* Interface Requests */ + + memset(&ifr, 0, sizeof(ifr)); + /* Set up interface flags */ + strcpy(ifr.ifr_name, savname+4); + ifr.ifr_flags = IFF_TAP|IFF_NO_PI; + + // Send interface requests to TUN/TAP driver. + if (ioctl(tun, TUNSETIFF, &ifr) >= 0) { + if (ioctl(tun, FIONBIO, &on)) { + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + close(tun); + } else { + dev->fd_handle = tun; + strcpy(savname, ifr.ifr_name); + } + } else + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + } else + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); +#elif defined(USE_BSDTUNTAP) && defined(USE_TAP_NETWORK) + snprintf(dev_name, sizeof(dev_name)-1, "/dev/%s", savname+4); + dev_name[sizeof(dev_name)-1] = '\0'; + + if ((tun = open(dev_name, O_RDWR)) >= 0) { + if (ioctl(tun, FIONBIO, &on)) { + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + close(tun); + } else { + dev->fd_handle = tun; + strcpy(savname, savname+4); + } + } else { + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + } +#else + strncpy(errbuf, "No support for tap: devices", sizeof(errbuf)-1); +#endif /* !defined(__linux) && !defined(USE_BSDTUNTAP) */ + } + if (errbuf[0]) { + msg = "Eth: open error - %s\r\n"; printf (msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); return SCPE_OPENERR; - } else { - msg = "Eth: opened %s\r\n"; - printf (msg, savname); - if (sim_log) fprintf (sim_log, msg, savname); } + msg = "Eth: opened OS device %s\r\n"; + printf (msg, savname); + if (sim_log) fprintf (sim_log, msg, savname); + + /* get the NIC's hardware MAC address */ + eth_get_nic_hw_addr(dev, savname); /* save name of device */ dev->name = malloc(strlen(savname)+1); @@ -948,7 +1428,7 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) #if !defined(HAS_PCAP_SENDPACKET) && defined (xBSD) && !defined (__APPLE__) /* Tell the kernel that the header is fully-formed when it gets it. This is required in order to fake the src address. */ - { + if (dev->pcap_mode) { int one = 1; ioctl(pcap_fileno(dev->handle), BIOCSHDRCMPLT, &one); } @@ -958,23 +1438,40 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) { pthread_attr_t attr; +#if defined(_WIN32) + pcap_setmintocopy (dev->handle, 0); +#endif ethq_init (&dev->read_queue, 200); /* initialize FIFO queue */ pthread_mutex_init (&dev->lock, NULL); + pthread_mutex_init (&dev->writer_lock, NULL); + pthread_cond_init (&dev->writer_cond, NULL); pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); pthread_create (&dev->reader_thread, &attr, _eth_reader, (void *)dev); + pthread_create (&dev->writer_thread, &attr, _eth_writer, (void *)dev); pthread_attr_destroy(&attr); } #else /* !defined (USE_READER_THREAD */ #ifdef USE_SETNONBLOCK /* set ethernet device non-blocking so pcap_dispatch() doesn't hang */ - if (pcap_setnonblock (dev->handle, 1, errbuf) == -1) { + if (dev->pcap_mode && (pcap_setnonblock (dev->handle, 1, errbuf) == -1)) { msg = "Eth: Failed to set non-blocking: %s\r\n"; printf (msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); } #endif #endif /* !defined (USE_READER_THREAD */ +#if defined (__APPLE__) + if (1) { + /* Deliver packets immediately, needed for OS X 10.6.2 and later + * (Snow-Leopard). + * See this thread on libpcap and Mac Os X 10.6 Snow Leopard on + * the tcpdump mailinglist: http://seclists.org/tcpdump/2010/q1/110 + */ + int v = 1; + ioctl(pcap_fileno(dev->handle), BIOCIMMEDIATE, &v); + } +#endif return SCPE_OK; } @@ -982,6 +1479,7 @@ t_stat eth_close(ETH_DEV* dev) { char* msg = "Eth: closed %s\r\n"; pcap_t *pcap; + int pcap_fd = dev->fd_handle; /* make sure device exists */ if (!dev) return SCPE_UNATT; @@ -989,14 +1487,40 @@ t_stat eth_close(ETH_DEV* dev) /* close the device */ pcap = (pcap_t *)dev->handle; dev->handle = NULL; - pcap_close(pcap); - printf (msg, dev->name); - if (sim_log) fprintf (sim_log, msg, dev->name); + dev->fd_handle = 0; + dev->have_host_nic_phy_addr = 0; #if defined (USE_READER_THREAD) pthread_join (dev->reader_thread, NULL); + pthread_mutex_destroy (&dev->lock); + pthread_cond_signal (&dev->writer_cond); + pthread_join (dev->writer_thread, NULL); + pthread_mutex_destroy (&dev->writer_lock); + pthread_cond_destroy (&dev->writer_cond); + if (1) { + struct write_request *buffer; + + while (buffer = dev->write_buffers) { + dev->write_buffers = buffer->next; + free(buffer); + } + while (buffer = dev->write_requests) { + dev->write_requests = buffer->next; + free(buffer); + } + } + ethq_destroy (&dev->read_queue); /* release FIFO queue */ #endif + if (dev->pcap_mode) + pcap_close(pcap); +#ifdef USE_TAP_NETWORK + else + close(pcap_fd); +#endif + printf (msg, dev->name); + if (sim_log) fprintf (sim_log, msg, dev->name); + /* clean up the mess */ free(dev->name); eth_zero(dev); @@ -1004,27 +1528,30 @@ t_stat eth_close(ETH_DEV* dev) return SCPE_OK; } -t_stat eth_reflect(ETH_DEV* dev, ETH_MAC mac) +t_stat eth_check_address_conflict (ETH_DEV* dev, + ETH_MAC* const mac) { ETH_PACK send, recv; t_stat status; - int i; - struct timeval delay; + int responses = 0; - /* build a packet */ + sim_debug(dev->dbit, dev->dptr, "Determining Address Conflict...\n"); + + /* build a loopback forward request packet */ memset (&send, 0, sizeof(ETH_PACK)); send.len = ETH_MIN_PACKET; /* minimum packet size */ memcpy(&send.msg[0], mac, sizeof(ETH_MAC)); /* target address */ memcpy(&send.msg[6], mac, sizeof(ETH_MAC)); /* source address */ send.msg[12] = 0x90; /* loopback packet type */ - for (i=14; ireflections = 0; eth_filter(dev, 1, (ETH_MAC *)mac, 0, 0); /* send the packet */ - status = eth_write (dev, &send, NULL); + status = _eth_write (dev, &send, NULL); if (status != SCPE_OK) { char *msg; msg = "Eth: Error Transmitting packet: %s\r\n" @@ -1035,24 +1562,38 @@ t_stat eth_reflect(ETH_DEV* dev, ETH_MAC mac) return status; } - /* if/when we have a sim_os_msleep() we'll use it here instead of this select() */ - delay.tv_sec = 0; - delay.tv_usec = 50*1000; - select(0, NULL, NULL, NULL, &delay); /* make sure things settle into the read path */ + sim_os_ms_sleep (300); /* time for a conflicting host to respond */ - /* empty the read queue and count the reflections */ + /* empty the read queue and count the responses */ do { memset (&recv, 0, sizeof(ETH_PACK)); status = eth_read (dev, &recv, NULL); - if (memcmp(send.msg, recv.msg, ETH_MIN_PACKET)== 0) - dev->reflections++; + if (memcmp(send.msg, recv.msg, 14)== 0) + responses++; } while (recv.len > 0); + sim_debug(dev->dbit, dev->dptr, "Address Conflict = %d\n", responses); + return responses; +} + +t_stat eth_reflect(ETH_DEV* dev) +{ + /* Test with an address no NIC should have. */ + /* We do this to avoid reflections from the wire, */ + /* in the event that a simulated NIC has a MAC address conflict. */ + ETH_MAC mac = {0xfe,0xff,0xff,0xff,0xff,0xfe}; + + sim_debug(dev->dbit, dev->dptr, "Determining Reflections...\n"); + + dev->reflections = 0; + dev->reflections = eth_check_address_conflict (dev, &mac); + sim_debug(dev->dbit, dev->dptr, "Reflections = %d\n", dev->reflections); return dev->reflections; } -t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) +static +t_stat _eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { int status = 1; /* default to failure */ @@ -1067,11 +1608,18 @@ t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) eth_packet_trace (dev, packet->msg, packet->len, "writing"); /* dispatch write request (synchronous; no need to save write info to dev) */ - status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len); + if (dev->pcap_mode) + status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len); +#ifdef USE_TAP_NETWORK + else + status = ((packet->len == write(dev->fd_handle, (void *)packet->msg, packet->len)) ? 0 : -1); +#endif - /* detect sending of decnet loopback packet */ - if ((status == 0) && DECNET_SELF_FRAME(dev->decnet_addr, packet->msg)) - dev->decnet_self_sent += dev->reflections; + /* detect sending of loopback packet */ + if ((status == 0) && (LOOPBACK_SELF_FRAME(dev->physical_addr, packet->msg))) { + dev->loopback_self_sent += dev->reflections; + dev->loopback_self_sent_total++; + } } /* if packet->len */ @@ -1082,19 +1630,480 @@ t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) return ((status == 0) ? SCPE_OK : SCPE_IOERR); } -void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data) +t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) +{ +#ifdef USE_READER_THREAD + struct write_request *request; + int write_queue_size = 1; + + /* make sure device exists */ + if (!dev) return SCPE_UNATT; + + /* Get a buffer */ + pthread_mutex_lock (&dev->writer_lock); + if (request = dev->write_buffers) + dev->write_buffers = request->next; + pthread_mutex_unlock (&dev->writer_lock); + if (!request) + request = malloc(sizeof(*request)); + + /* Copy buffer contents */ + request->packet.len = packet->len; + request->packet.used = packet->used; + request->packet.status = packet->status; + request->packet.crc_len = packet->crc_len; + memcpy(request->packet.msg, packet->msg, packet->len); + + /* Insert buffer at the end of the write list (to make sure that */ + /* packets make it to the wire in the order they were presented here) */ + pthread_mutex_lock (&dev->writer_lock); + request->next = NULL; + if (dev->write_requests) { + struct write_request *last_request = dev->write_requests; + + ++write_queue_size; + while (last_request->next) { + last_request = last_request->next; + ++write_queue_size; + } + last_request->next = request; + } else + dev->write_requests = request; + if (write_queue_size > dev->write_queue_peak) + dev->write_queue_peak = write_queue_size; + pthread_mutex_unlock (&dev->writer_lock); + + /* Awaken writer thread to perform actual write */ + pthread_cond_signal (&dev->writer_cond); + + /* Return with a status from some prior write */ + if (routine) + (routine)(dev->write_status); + return dev->write_status; +#else + return _eth_write(dev, packet, routine); +#endif +} + +static int +_eth_hash_lookup(ETH_MULTIHASH hash, const u_char* data) +{ + int key = 0x3f & (eth_crc32(0, data, 6) >> 26); + + key ^= 0x3f; + return (hash[key>>3] & (1 << (key&0x7))); +} + +static int +_eth_hash_validate(ETH_MAC *MultiCastList, int count, ETH_MULTIHASH hash) +{ + ETH_MULTIHASH lhash; + int i; + + memset(lhash, 0, sizeof(lhash)); + for (i=0; i> 26); + + key ^= 0x3F; + printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X Key: %X, Byte: %X, Val: %X\n", + MultiCastList[i][0], MultiCastList[i][1], MultiCastList[i][2], MultiCastList[i][3], MultiCastList[i][4], MultiCastList[i][5], + key, key>>3, (1 << (key&0x7))); + lhash[key>>3] |= (1 << (key&0x7)); + } + if (memcmp(hash, lhash, sizeof(lhash))) { + printf("Inconsistent Computed Hash:\n"); + printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", + hash[0], hash[1], hash[2], hash[3], + hash[4], hash[5], hash[6], hash[7]); + printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", + lhash[0], lhash[1], lhash[2], lhash[3], + lhash[4], lhash[5], lhash[6], lhash[7]); + } + printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", + hash[0], hash[1], hash[2], hash[3], + hash[4], hash[5], hash[6], hash[7]); + printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", + lhash[0], lhash[1], lhash[2], lhash[3], + lhash[4], lhash[5], lhash[6], lhash[7]); + return 0; +} + +static void +_eth_test_multicast_hash() +{ + ETH_MAC tMacs[] = { + {0xAB, 0x00, 0x04, 0x01, 0xAC, 0x10}, + {0xAB, 0x00, 0x00, 0x04, 0x00, 0x00}, + {0x09, 0x00, 0x2B, 0x00, 0x00, 0x0F}, + {0x09, 0x00, 0x2B, 0x02, 0x01, 0x04}, + {0x09, 0x00, 0x2B, 0x02, 0x01, 0x07}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {0x01, 0x00, 0x5E, 0x00, 0x00, 0x01}}; + ETH_MULTIHASH thash = {0x01, 0x40, 0x00, 0x00, 0x48, 0x88, 0x40, 0x00}; + + _eth_hash_validate(tMacs, sizeof(tMacs)/sizeof(tMacs[0]), thash); +} + +/* The IP header */ +struct IPHeader { + uint8 verhlen; /* Version & Header Length in dwords */ +#define IP_HLEN(IP) (((IP)->verhlen&0xF)<<2) /* Header Length in Bytes */ +#define IP_VERSION(IP) ((((IP)->verhlen)>>4)&0xF) /* IP Version */ + uint8 tos; /* Type of service */ + uint16 total_len; /* Length of the packet in dwords */ + uint16 ident; /* unique identifier */ + uint16 flags; /* Fragmentation Flags */ +#define IP_DF_FLAG (0x4000) +#define IP_MF_FLAG (0x2000) +#define IP_OFFSET_MASK (0x1FFF) +#define IP_FRAG_DF(IP) (ntohs(((IP)->flags))&IP_DF_FLAG) +#define IP_FRAG_MF(IP) (ntohs(((IP)->flags))&IP_MF_FLAG) +#define IP_FRAG_OFFSET(IP) (ntohs(((IP)->flags))&IP_OFFSET_MASK) + uint8 ttl; /* Time to live */ + uint8 proto; /* Protocol number (TCP, UDP etc) */ + uint16 checksum; /* IP checksum */ + uint32 source_ip; /* Source Address */ + uint32 dest_ip; /* Destination Address */ +}; + +/* ICMP header */ +struct ICMPHeader { + uint8 type; /* ICMP packet type */ + uint8 code; /* Type sub code */ + uint16 checksum; /* ICMP Checksum */ + uint32 otherstuff[1];/* optional data */ +}; + +struct UDPHeader { + uint16 source_port; + uint16 dest_port; + uint16 length; /* The length of the entire UDP datagram, including both header and Data fields. */ + uint16 checksum; +}; + +struct TCPHeader { + uint16 source_port; + uint16 dest_port; + uint32 sequence_number; + uint32 acknowledgement_number; + uint16 data_offset_and_flags; +#define TCP_DATA_OFFSET(TCP) ((ntohs((TCP)->data_offset_and_flags)>>12)<<2) +#define TCP_CWR_FLAG (0x80) +#define TCP_ECR_FLAG (0x40) +#define TCP_URG_FLAG (0x20) +#define TCP_ACK_FLAG (0x10) +#define TCP_PSH_FLAG (0x08) +#define TCP_RST_FLAG (0x04) +#define TCP_SYN_FLAG (0x02) +#define TCP_FIN_FLAG (0x01) +#define TCP_FLAGS_MASK (0xFF) + uint16 window; + uint16 checksum; + uint16 urgent; + uint16 otherstuff[1]; /* The rest of the packet */ +}; + +#ifndef IPPROTO_TCP +#define IPPROTO_TCP 6 /* tcp */ +#endif +#ifndef IPPROTO_UDP +#define IPPROTO_UDP 17 /* user datagram protocol */ +#endif +#ifndef IPPROTO_ICMP +#define IPPROTO_ICMP 1 /* control message protocol */ +#endif + +static uint16 +ip_checksum(uint16 *buffer, int size) +{ + unsigned long cksum = 0; + + /* Sum all the words together, adding the final byte if size is odd */ + while (size > 1) { + cksum += *buffer++; + size -= sizeof(*buffer); + } + if (size) { + uint8 endbytes[2]; + + endbytes[0] = *((uint8 *)buffer); + endbytes[1] = 0; + cksum += *((uint16 *)endbytes); + } + + /* Do a little shuffling */ + cksum = (cksum >> 16) + (cksum & 0xffff); + cksum += (cksum >> 16); + + /* Return the bitwise complement of the resulting mishmash */ + return (uint16)(~cksum); +} + +static uint16 +pseudo_checksum(uint16 len, uint16 proto, uint16 *src_addr, uint16 *dest_addr, uint8 *buff) +{ + uint32 sum; + + /* Sum the data first */ + sum = 0xffff&(~ip_checksum((uint16 *)buff, len)); + + /* add the pseudo header which contains the IP source and destinationn addresses */ + sum += src_addr[0]; + sum += src_addr[1]; + sum += dest_addr[0]; + sum += dest_addr[1]; + /* and the protocol number and the length of the UDP packet */ + sum = sum + htons(proto) + htons(len); + + /* Do a little shuffling */ + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + + /* Return the bitwise complement of the resulting mishmash */ + return (uint16)(~sum); +} + +static void +_eth_fix_ip_jumbo_offload(ETH_DEV* dev, const u_char* msg, int len) +{ + unsigned short* proto = (unsigned short*) &msg[12]; + struct IPHeader *IP; + struct TCPHeader *TCP = NULL; + struct UDPHeader *UDP; + struct ICMPHeader *ICMP; + uint16 orig_checksum; + uint16 payload_len; + uint16 mtu_payload; + uint16 ip_flags; + uint16 frag_offset; + struct pcap_pkthdr header; + uint16 tcp_flags; + + /* Only interested in IP frames */ + if (ntohs(*proto) != 0x0800) { + ++dev->jumbo_dropped; /* Non IP Frames are dropped */ + return; + } + IP = (struct IPHeader *)&msg[14]; + if (IP_VERSION(IP) != 4) { + ++dev->jumbo_dropped; /* Non IPv4 jumbo frames are dropped */ + return; + } + if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) { + ++dev->jumbo_dropped; /* Bogus header length frames are dropped */ + return; + } + if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) { + ++dev->jumbo_dropped; /* Previously fragmented, but currently jumbo sized frames are dropped */ + return; + } + switch (IP->proto) { + case IPPROTO_UDP: + UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); + if (ntohs(UDP->length) > (len-IP_HLEN(IP))) { + ++dev->jumbo_dropped; /* Bogus UDP packet length (packet contained length exceeds packet size) frames are dropped */ + return; + } + if (UDP->checksum == 0) + break; /* UDP Cghecksums are disabled */ + orig_checksum = UDP->checksum; + UDP->checksum = 0; + UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); + if (orig_checksum != UDP->checksum) + eth_packet_trace (dev, msg, len, "reading jumbo UDP header Checksum Fixed"); + break; + case IPPROTO_ICMP: + ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_checksum = ICMP->checksum; + ICMP->checksum = 0; + ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); + if (orig_checksum != ICMP->checksum) + eth_packet_trace (dev, msg, len, "reading jumbo ICMP header Checksum Fixed"); + break; + case IPPROTO_TCP: + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + if ((TCP_DATA_OFFSET(TCP) > (len-IP_HLEN(IP))) || (TCP_DATA_OFFSET(TCP) < 20)) { + ++dev->jumbo_dropped; /* Bogus TCP packet header length (packet contained length exceeds packet size) frames are dropped */ + return; + } + /* We don't do anything with the TCP checksum since we're going to resegment the TCP data below */ + break; + default: + ++dev->jumbo_dropped; /* We onlt handle UDP, ICMP and TCP jumbo frames others are dropped */ + return; + } + /* Reasonable Checksums are now in the jumbo packet, but we've got to actually */ + /* deliver ONLY standard sized ethernet frames. Our job here is to now act as */ + /* a router might have to and fragment these IPv4 frames as they are delivered */ + /* into the virtual NIC. We do this by walking down the packet and dispatching */ + /* a chunk at a time recomputing an appropriate header for each chunk. For */ + /* datagram oriented protocols (UDP and ICMP) this is done by simple packet */ + /* fragmentation. For TCP this is done by breaking large packets into separate */ + /* TCP packets. */ + memset(&header, 0, sizeof(header)); + switch (IP->proto) { + case IPPROTO_UDP: + case IPPROTO_ICMP: + ++dev->jumbo_fragmented; + payload_len = ntohs(IP->total_len) - IP_HLEN(IP); + mtu_payload = ETH_MIN_JUMBO_FRAME - 14 - IP_HLEN(IP); + frag_offset = 0; + while (payload_len > 0) { + ip_flags = frag_offset; + if (payload_len > mtu_payload) { + ip_flags |= IP_MF_FLAG; + IP->total_len = htons(((mtu_payload>>3)<<3) + IP_HLEN(IP)); + } else { + IP->total_len = htons(payload_len + IP_HLEN(IP)); + } + IP->flags = htons(ip_flags); + IP->checksum = 0; + IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); + header.len = 14 + ntohs(IP->total_len); + eth_packet_trace (dev, ((u_char *)IP)-14, header.len, "reading Datagram fragment"); +#if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET + { /* Debugging is easier it we read packets directly with pcap */ + ETH_PACK pkt; + + memset(&pkt, 0, sizeof(pkt)); + memcpy(pkt.msg, ((u_char *)IP)-14, header.len); + pkt.len = header.len; + _eth_write(dev, &pkt, NULL); + } +#else + _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); +#endif + payload_len -= (ntohs(IP->total_len) - IP_HLEN(IP)); + frag_offset += (ntohs(IP->total_len) - IP_HLEN(IP))>>3; + if (payload_len > 0) { + /* Move the MAC and IP headers down to just prior to the next payload segment */ + memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP)); + IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - IP_HLEN(IP)); + } + } + break; + case IPPROTO_TCP: + ++dev->jumbo_fragmented; + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + tcp_flags = ntohs(TCP->data_offset_and_flags)&TCP_FLAGS_MASK; + TCP->data_offset_and_flags = htons(((TCP_DATA_OFFSET(TCP)>>2)<<12)|TCP_ACK_FLAG); + payload_len = ntohs(IP->total_len) - IP_HLEN(IP) - TCP_DATA_OFFSET(TCP); + mtu_payload = ETH_MIN_JUMBO_FRAME - 14 - IP_HLEN(IP) - TCP_DATA_OFFSET(TCP); + while (payload_len > 0) { + if (payload_len > mtu_payload) { + IP->total_len = htons(mtu_payload + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + } else { + TCP->data_offset_and_flags = htons(ntohs(TCP->data_offset_and_flags)|(tcp_flags&(TCP_PSH_FLAG|TCP_ACK_FLAG|TCP_FIN_FLAG|TCP_RST_FLAG))); + IP->total_len = htons(payload_len + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + } + IP->checksum = 0; + IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); + TCP->checksum = 0; + TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); + header.len = 14 + ntohs(IP->total_len); + eth_packet_trace_ex (dev, ((u_char *)IP)-14, header.len, "reading TCP segment", 1, dev->dbit); +#if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET + { /* Debugging is easier it we read packets directly with pcap */ + ETH_PACK pkt; + + memset(&pkt, 0, sizeof(pkt)); + memcpy(pkt.msg, ((u_char *)IP)-14, header.len); + pkt.len = header.len; + _eth_write(dev, &pkt, NULL); + } +#else + _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); +#endif + payload_len -= (ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); + if (payload_len > 0) { + /* Move the MAC, IP and TCP headers down to just prior to the next payload segment */ + memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + TCP->sequence_number = htonl(mtu_payload + ntohl(TCP->sequence_number)); + } + } + break; + } +} + +static void +_eth_fix_ip_xsum_offload(ETH_DEV* dev, u_char* msg, int len) +{ + unsigned short* proto = (unsigned short*) &msg[12]; + struct IPHeader *IP; + struct TCPHeader *TCP; + struct UDPHeader *UDP; + struct ICMPHeader *ICMP; + uint16 orig_checksum; + + /* Only need to process locally originated packets */ + if ((!dev->have_host_nic_phy_addr) || (memcmp(msg+6, dev->host_nic_phy_hw_addr, 6))) + return; + /* Only interested in IP frames */ + if (ntohs(*proto) != 0x0800) + return; + IP = (struct IPHeader *)&msg[14]; + if (IP_VERSION(IP) != 4) + return; /* Only interested in IPv4 frames */ + if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) + return; /* Bogus header length */ + orig_checksum = IP->checksum; + IP->checksum = 0; + IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); + if (orig_checksum != IP->checksum) + eth_packet_trace (dev, msg, len, "reading IP header Checksum Fixed"); + if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) + return; /* Insufficient data to compute payload checksum */ + switch (IP->proto) { + case IPPROTO_UDP: + UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); + if (ntohs(UDP->length) > (len-IP_HLEN(IP))) + return; /* packet contained length exceeds packet size */ + if (UDP->checksum == 0) + return; /* UDP Cghecksums are disabled */ + orig_checksum = UDP->checksum; + UDP->checksum = 0; + UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); + if (orig_checksum != UDP->checksum) + eth_packet_trace (dev, msg, len, "reading UDP header Checksum Fixed"); + break; + case IPPROTO_TCP: + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_checksum = TCP->checksum; + TCP->checksum = 0; + TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); + if (orig_checksum != TCP->checksum) + eth_packet_trace (dev, msg, len, "reading TCP header Checksum Fixed"); + break; + case IPPROTO_ICMP: + ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_checksum = ICMP->checksum; + ICMP->checksum = 0; + ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); + if (orig_checksum != ICMP->checksum) + eth_packet_trace (dev, msg, len, "reading ICMP header Checksum Fixed"); + break; + } +} + +static void +_eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data) { ETH_DEV* dev = (ETH_DEV*) info; #ifdef USE_BPF int to_me = 1; + + /* AUTODIN II hash mode? */ + if ((dev->hash_filter) && (data[0] & 0x01) && (!dev->promiscuous) && (!dev->all_multicast)) + to_me = _eth_hash_lookup(dev->hash, data); #else /* !USE_BPF */ int to_me = 0; int from_me = 0; int i; -#ifdef ETH_DEBUG -// eth_packet_trace (dev, data, header->len, "received"); -#endif + eth_packet_trace (dev, data, header->len, "received"); + for (i = 0; i < dev->addr_count; i++) { if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1; if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1; @@ -1105,13 +2114,19 @@ void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* /* promiscuous mode? */ if (dev->promiscuous) to_me = 1; + + /* AUTODIN II hash mode? */ + if ((dev->hash_filter) && (!to_me) && (data[0] & 0x01)) + to_me = _eth_hash_lookup(dev->hash, data); #endif /* USE_BPF */ - /* detect sending of decnet loopback packet */ - if (DECNET_SELF_FRAME(dev->decnet_addr, data)) { - /* lower reflection count - if already zero, pass it on */ - if (dev->decnet_self_sent > 0) { - dev->decnet_self_sent--; + /* detect reception of loopback packet to our physical address */ + if (LOOPBACK_SELF_FRAME(dev->physical_addr, data)) { + dev->loopback_self_rcvd_total++; + /* lower reflection count - if already zero, pass it on */ + if (dev->loopback_self_sent > 0) { + eth_packet_trace (dev, data, header->len, "ignored"); + dev->loopback_self_sent--; to_me = 0; } #ifndef USE_BPF @@ -1125,26 +2140,60 @@ void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* #else /* !USE_BPF */ if (to_me && !from_me) { #endif + if (header->len > ETH_MIN_JUMBO_FRAME) { + _eth_fix_ip_jumbo_offload(dev, data, header->len); + return; + } #if defined (USE_READER_THREAD) - ETH_PACK tmp_packet; + if (1) { + int crc_len = 0; + uint8 crc_data[4]; + uint32 len = header->len; + u_char* moved_data = NULL; - /* set data in passed read packet */ - tmp_packet.len = header->len; - memcpy(tmp_packet.msg, data, header->len); - if (dev->need_crc) - eth_add_crc32(&tmp_packet); + if (header->len < ETH_MIN_PACKET) { /* Pad runt packets before CRC append */ + moved_data = malloc(ETH_MIN_PACKET); + memcpy(moved_data, data, len); + memset(moved_data + len, 0, ETH_MIN_PACKET-len); + len = ETH_MIN_PACKET; + data = moved_data; + } - eth_packet_trace (dev, tmp_packet.msg, tmp_packet.len, "rcvqd"); + /* If necessary, fix IP header checksums for packets originated locally */ + /* but were presumed to be traversing a NIC which was going to handle that task */ + /* This must be done before any needed CRC calculation */ + _eth_fix_ip_xsum_offload(dev, (u_char*)data, len); + + if (dev->need_crc) + crc_len = eth_get_packet_crc32_data(data, len, crc_data); - pthread_mutex_lock (&dev->lock); - ethq_insert(&dev->read_queue, 2, &tmp_packet, 0); - pthread_mutex_unlock (&dev->lock); + eth_packet_trace (dev, data, len, "rcvqd"); + + pthread_mutex_lock (&dev->lock); + ethq_insert_data(&dev->read_queue, 2, data, 0, len, crc_len, crc_data, 0); + pthread_mutex_unlock (&dev->lock); + free(moved_data); + } #else /* set data in passed read packet */ dev->read_packet->len = header->len; memcpy(dev->read_packet->msg, data, header->len); + /* Handle runt case and pad with zeros. */ + /* The real NIC won't hand us runts from the wire, BUT we may be getting */ + /* some packets looped back before they actually traverse the wire */ + /* (by an internal bridge device for instance) */ + if (header->len < ETH_MIN_PACKET) { + memset(&dev->read_packet->msg[header->len], 0, ETH_MIN_PACKET-header->len); + dev->read_packet->len = ETH_MIN_PACKET; + } + /* If necessary, fix IP header checksums for packets originated by the local host */ + /* but were presumed to be traversing a NIC which was going to handle that task */ + /* This must be done before any needed CRC calculation */ + _eth_fix_ip_xsum_offload(dev, dev->read_packet->msg, dev->read_packet->len); if (dev->need_crc) - eth_add_crc32(dev->read_packet); + dev->read_packet->crc_len = eth_add_packet_crc32(dev->read_packet->msg, dev->read_packet->len); + else + dev->read_packet->crc_len = 0; eth_packet_trace (dev, dev->read_packet->msg, dev->read_packet->len, "reading"); @@ -1155,28 +2204,46 @@ void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* } } -t_stat eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) +int eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { int status; /* make sure device exists */ - if (!dev) return SCPE_UNATT; + if (!dev) return 0; /* make sure packet exists */ - if (!packet) return SCPE_ARG; + if (!packet) return 0; + packet->len = 0; #if !defined (USE_READER_THREAD) /* set read packet */ dev->read_packet = packet; - packet->len = 0; /* set optional callback routine */ dev->read_callback = routine; /* dispatch read request to either receive a filtered packet or timeout */ do { - status = pcap_dispatch((pcap_t*)dev->handle, 1, ð_callback, (u_char*)dev); + if (dev->pcap_mode) + status = pcap_dispatch((pcap_t*)dev->handle, 1, &_eth_callback, (u_char*)dev); +#ifdef USE_TAP_NETWORK + else { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; + + memset(&header, 0, sizeof(header)); + len = read(dev->fd_handle, buf, sizeof(buf)); + if (len > 0) { + status = 1; + header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } else { + status = 0; + } + } +#endif /* USE_TAP_NETWORK */ } while ((status) && (0 == packet->len)); #else /* USE_READER_THREAD */ @@ -1186,23 +2253,34 @@ t_stat eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) if (dev->read_queue.count > 0) { ETH_ITEM* item = &dev->read_queue.item[dev->read_queue.head]; packet->len = item->packet.len; - memcpy(packet->msg, item->packet.msg, packet->len); - if (routine) - routine(status); + packet->crc_len = item->packet.crc_len; + memcpy(packet->msg, item->packet.msg, ((packet->len > packet->crc_len) ? packet->len : packet->crc_len)); + status = 1; ethq_remove(&dev->read_queue); } pthread_mutex_unlock (&dev->lock); + if ((status) && (routine)) + routine(0); #endif - return SCPE_OK; + return status; } -t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, +t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous) +{ +return eth_filter_hash(dev, addr_count, addresses, + all_multicast, promiscuous, + NULL); +} + +t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, + ETH_BOOL all_multicast, ETH_BOOL promiscuous, + ETH_MULTIHASH* const hash) { int i; bpf_u_int32 bpf_subnet, bpf_netmask; - char buf[110+66*ETH_FILTER_MAX]; + char buf[114+66*ETH_FILTER_MAX]; char errbuf[PCAP_ERRBUF_SIZE]; char mac[20]; char* buf2; @@ -1221,6 +2299,11 @@ t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, else if (!addresses) return SCPE_ARG; + /* test reflections. This is done early in this routine since eth_reflect */ + /* calls eth_filter recursively and thus changes the state of the device. */ + if (dev->reflections == -1) + status = eth_reflect(dev); + /* set new filter addresses */ for (i = 0; i < addr_count; i++) memcpy(dev->filter_address[i], addresses[i], sizeof(ETH_MAC)); @@ -1230,6 +2313,15 @@ t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, dev->all_multicast = all_multicast; dev->promiscuous = promiscuous; + /* store multicast hash data */ + dev->hash_filter = (hash != NULL); + if (hash) { + memcpy(dev->hash, hash, sizeof(*hash)); + sim_debug(dev->dbit, dev->dptr, "Multicast Hash: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n", + dev->hash[0], dev->hash[1], dev->hash[2], dev->hash[3], + dev->hash[4], dev->hash[5], dev->hash[6], dev->hash[7]); + } + /* print out filter information if debugging */ if (dev->dptr->dctrl & dev->dbit) { sim_debug(dev->dbit, dev->dptr, "Filter Set\n"); @@ -1244,10 +2336,6 @@ t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, sim_debug(dev->dbit, dev->dptr, "Promiscuous\n"); } - /* test reflections */ - if (dev->reflections == -1) - status = eth_reflect(dev, dev->filter_address[0]); - /* setup BPF filters and other fields to minimize packet delivery */ strcpy(buf, ""); @@ -1258,10 +2346,12 @@ t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, for (i = 0; i < addr_count; i++) { eth_mac_fmt(&dev->filter_address[i], mac); if (!strstr(buf, mac)) /* eliminate duplicates */ - sprintf(&buf[strlen(buf)], "%s(ether dst %s)", (*buf) ? " or " : "", mac); + sprintf(&buf[strlen(buf)], "%s(ether dst %s)", (*buf) ? " or " : "((", mac); } - if (dev->all_multicast) - sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : ""); + if (dev->all_multicast || dev->hash_filter) + sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : "(("); + if (strlen(buf) > 0) + sprintf(&buf[strlen(buf)], ")"); } /* construct source filters - this prevents packets from being reflected back @@ -1281,66 +2371,84 @@ t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, } sprintf (&buf[strlen(buf)], ")"); } - /* When starting, DECnet sends out a packet with the source and destination - addresses set to the same value as the DECnet MAC address. This packet is - designed to find and help diagnose DECnet address conflicts. Normally, this - packet would not be seen by the sender, only by the other machine that has - the same DECnet address. If the ethernet subsystem is reflecting packets, - DECnet will fail to start if it sees the reflected packet, since it thinks - another system is using this DECnet address. We have to let these packets - through, so that if another machine has the same DECnet address that we - can detect it. Both eth_write() and eth_callback() help by checking the - reflection count - eth_write() adds the reflection count to - dev->decnet_self_sent, and eth_callback() check the value - if the - dev->decnet_self_sent count is zero, then the packet has come from another - machine with the same address, and needs to be passed on to the simulated - machine. */ - memset(dev->decnet_addr, 0, sizeof(ETH_MAC)); - /* check for decnet address in filters */ + if (strlen(buf) > 0) + sprintf(&buf[strlen(buf)], ")"); + /* When changing the Physical Address on a LAN interface, VMS sends out a + loopback packet with the source and destination addresses set to the same + value as the Physical Address which is being setup. This packet is + designed to find and help diagnose MAC address conflicts (which also + include DECnet address conflicts). Normally, this packet would not be + seen by the sender, only by the other machine that has the same Physical + Address (or possibly DECnet address). If the ethernet subsystem is + reflecting packets, the network startup will fail to start if it sees the + reflected packet, since it thinks another system is using this Physical + Address (or DECnet address). We have to let these packets through, so + that if another machine has the same Physical Address (or DECnet address) + that we can detect it. Both eth_write() and _eth_callback() help by + checking the reflection count - eth_write() adds the reflection count to + dev->loopback_self_sent, and _eth_callback() check the value - if the + dev->loopback_self_sent count is zero, then the packet has come from + another machine with the same address, and needs to be passed on to the + simulated machine. */ + memset(dev->physical_addr, 0, sizeof(ETH_MAC)); + dev->loopback_self_sent = 0; + /* check for physical address in filters */ if ((addr_count) && (dev->reflections > 0)) { for (i = 0; i < addr_count; i++) { + if (dev->filter_address[i][0]&1) + continue; /* skip all multicast addresses */ eth_mac_fmt(&dev->filter_address[i], mac); - if (memcmp(mac, "AA:00:04", 8) == 0) { - memcpy(dev->decnet_addr, &dev->filter_address[i], sizeof(ETH_MAC)); - /* let packets through where dst and src are the same as our decnet address */ + if (strcmp(mac, "00:00:00:00:00:00") != 0) { + memcpy(dev->physical_addr, &dev->filter_address[i], sizeof(ETH_MAC)); + /* let packets through where dst and src are the same as our physical address */ sprintf (&buf[strlen(buf)], " or ((ether dst %s) and (ether src %s))", mac, mac); break; } } } + if ((0 == strlen(buf)) && (!dev->promiscuous)) /* Empty filter means match nothing */ + strcpy(buf, "ether host fe:ff:ff:ff:ff:ff"); /* this should be a good match nothing filter */ sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf); - /* get netmask, which is required for compiling */ - if (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0) { + if (dev->pcap_mode && (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0)) { bpf_netmask = 0; } #ifdef USE_BPF - /* compile filter string */ - if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { - sprintf(errbuf, "%s", pcap_geterr(dev->handle)); - msg = "Eth: pcap_compile error: %s\r\n"; - printf(msg, errbuf); - if (sim_log) fprintf (sim_log, msg, errbuf); - /* show erroneous BPF string */ - msg = "Eth: BPF string is: |%s|\r\n"; - printf (msg, buf); - if (sim_log) fprintf (sim_log, msg, buf); - } else { - /* apply compiled filter string */ - if ((status = pcap_setfilter(dev->handle, &bpf)) < 0) { + if (dev->pcap_mode) { +#ifdef USE_READER_THREAD + pthread_mutex_lock (&dev->lock); + ethq_clear (&dev->read_queue); /* Empty FIFO Queue when filter list changes */ + pthread_mutex_unlock (&dev->lock); +#endif + /* compile filter string */ + if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { sprintf(errbuf, "%s", pcap_geterr(dev->handle)); - msg = "Eth: pcap_setfilter error: %s\r\n"; + msg = "Eth: pcap_compile error: %s\r\n"; printf(msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); + sim_debug(dev->dbit, dev->dptr, "Eth: pcap_compile error: %s\n", errbuf); + /* show erroneous BPF string */ + msg = "Eth: BPF string is: |%s|\r\n"; + printf (msg, buf); + if (sim_log) fprintf (sim_log, msg, buf); } else { + /* apply compiled filter string */ + if ((status = pcap_setfilter(dev->handle, &bpf)) < 0) { + sprintf(errbuf, "%s", pcap_geterr(dev->handle)); + msg = "Eth: pcap_setfilter error: %s\r\n"; + printf(msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + sim_debug(dev->dbit, dev->dptr, "Eth: pcap_setfilter error: %s\n", errbuf); + } else { #ifdef USE_SETNONBLOCK - /* set file non-blocking */ - status = pcap_setnonblock (dev->handle, 1, errbuf); + /* set file non-blocking */ + status = pcap_setnonblock (dev->handle, 1, errbuf); #endif /* USE_SETNONBLOCK */ + } + pcap_freecode(&bpf); } - pcap_freecode(&bpf); } #endif /* USE_BPF */ @@ -1387,46 +2495,59 @@ int eth_host_devices(int used, int max, ETH_LIST* list) #if defined(_WIN32) /* replace device description with user-defined adapter name (if defined) */ for (i=0; i sizeof(regval))) { - RegCloseKey (reghnd); - continue; - } + if((regtype != REG_SZ) || (reglen > sizeof(regval))) { + RegCloseKey (reghnd); + continue; + } /* registry value seems OK, finish up and replace description */ - RegCloseKey (reghnd ); + RegCloseKey (reghnd ); sprintf (list[i].desc, "%s", regval); } } /* for */ #endif - return used; +#ifdef USE_TAP_NETWORK + if (used < max) { + list[used].num = used; +#if defined(__OpenBSD__) + sprintf(list[used].name, "%s", "tap:tunN"); +#else + sprintf(list[used].name, "%s", "tap:tapN"); +#endif + sprintf(list[used].desc, "%s", "Integrated Tun/Tap support"); + ++used; + } +#endif + + return used; } int eth_devices(int max, ETH_LIST* list) @@ -1467,4 +2588,35 @@ int eth_devices(int max, ETH_LIST* list) return i; } +void eth_show_dev (FILE *st, ETH_DEV* dev) +{ + fprintf(st, "Ethernet Device:\n"); + if (!dev) { + fprintf(st, "-- Not Attached\n"); + return; + } + fprintf(st, " Name: %s\n", dev->name); + fprintf(st, " Reflections: %d\n", dev->reflections); + fprintf(st, " Self Loopbacks Sent: %d\n", dev->loopback_self_sent_total); + fprintf(st, " Self Loopbacks Rcvd: %d\n", dev->loopback_self_rcvd_total); + if (dev->have_host_nic_phy_addr) { + char hw_mac[20]; + + eth_mac_fmt(&dev->host_nic_phy_hw_addr, hw_mac); + fprintf(st, " Host NIC Address: %s\n", hw_mac); + } + if (dev->jumbo_dropped) + fprintf(st, " Jumbo Dropped: %d\n", dev->jumbo_dropped); + if (dev->jumbo_fragmented) + fprintf(st, " Jumbo Fragmented: %d\n", dev->jumbo_fragmented); +#if defined(USE_READER_THREAD) + fprintf(st, " Asynch Interrupts: %s\n", dev->asynch_io?"Enabled":"Disabled"); + if (dev->asynch_io) + fprintf(st, " Interrupt Latency: %d uSec\n", dev->asynch_io_latency); + fprintf(st, " Read Queue: Count: %d\n", dev->read_queue.count); + fprintf(st, " Read Queue: High: %d\n", dev->read_queue.high); + fprintf(st, " Read Queue: Loss: %d\n", dev->read_queue.loss); + fprintf(st, " Peak Write Queue Size: %d\n", dev->write_queue_peak); +#endif +} #endif /* USE_NETWORK */ diff --git a/sim_ether.h b/sim_ether.h index 371ee3ba..4374f31e 100644 --- a/sim_ether.h +++ b/sim_ether.h @@ -28,6 +28,14 @@ Modification history: + 09-Dec-10 MP Added support to determine if network address conflicts exist + 07-Dec-10 MP Reworked DECnet self detection to the more general approach + of loopback self when any Physical Address is being set. + 04-Dec-10 MP Changed eth_write to do nonblocking writes when + USE_READER_THREAD is defined. + 07-Feb-08 MP Added eth_show_dev to display ethernet state + 28-Jan-08 MP Added eth_set_async + 23-Jan-08 MP Added eth_packet_trace_ex and ethq_destroy 30-Nov-05 DTH Added CRC length to packet and more field comments 04-Feb-04 DTH Added debugging information 14-Jan-04 MP Generalized BSD support issues @@ -64,7 +72,7 @@ #define USE_SETNONBLOCK 1 #endif -#if defined(__sun__) && defined(__i386__) +#if (((defined(__sun__) && defined(__i386__)) || defined(__linux)) && !defined(DONT_USE_READER_THREAD)) #define USE_READER_THREAD 1 #endif @@ -108,13 +116,20 @@ #define ETH_DEV_DESC_MAX 256 /* maximum device description size */ #define ETH_MIN_PACKET 60 /* minimum ethernet packet size */ #define ETH_MAX_PACKET 1514 /* maximum ethernet packet size */ +#define ETH_MAX_JUMBO_FRAME 16384 /* maximum ethernet jumbo frame size */ #define ETH_MAX_DEVICE 10 /* maximum ethernet devices */ #define ETH_CRC_SIZE 4 /* ethernet CRC size */ -#define ETH_FRAME_SIZE 1518 /* ethernet maximum frame size */ +#define ETH_FRAME_SIZE (ETH_MAX_PACKET+ETH_CRC_SIZE) /* ethernet maximum frame size */ +#define ETH_MIN_JUMBO_FRAME ETH_MAX_PACKET /* Threshold size for Jumbo Frame Processing */ -#define DECNET_SELF_FRAME(dnet_mac, msg) \ - ((memcmp(dnet_mac, msg , 6) == 0) && \ - (memcmp(dnet_mac, msg+6, 6) == 0)) +#define LOOPBACK_SELF_FRAME(phy_mac, msg) \ + ((memcmp(phy_mac, msg , 6) == 0) && \ + (memcmp(phy_mac, msg+6, 6) == 0) && \ + ((msg)[12] == 0x90) && ((msg)[13] == 0x00) && \ + ((msg)[14] == 0x00) && ((msg)[15] == 0x00) && \ + ((msg)[16] == 0x02) && ((msg)[17] == 0x00) && \ + (memcmp(phy_mac, msg+18, 6) == 0) && \ + ((msg)[24] == 0x01) && ((msg)[25] == 0x00)) struct eth_packet { uint8 msg[ETH_FRAME_SIZE]; /* ethernet frame (message) */ @@ -147,6 +162,7 @@ struct eth_list { typedef int ETH_BOOL; typedef unsigned char ETH_MAC[6]; +typedef unsigned char ETH_MULTIHASH[8]; typedef struct eth_packet ETH_PACK; typedef void (*ETH_PCALLBACK)(int status); typedef struct eth_list ETH_LIST; @@ -156,24 +172,45 @@ typedef struct eth_item ETH_ITEM; struct eth_device { char* name; /* name of ethernet device */ void* handle; /* handle of implementation-specific device */ + int fd_handle; /* fd to kernel device (where needed) */ + int pcap_mode; /* Flag indicating if pcap API are being used to move packets */ ETH_PCALLBACK read_callback; /* read callback function */ ETH_PCALLBACK write_callback; /* write callback function */ ETH_PACK* read_packet; /* read packet */ - ETH_PACK* write_packet; /* write packet */ ETH_MAC filter_address[ETH_FILTER_MAX]; /* filtering addresses */ int addr_count; /* count of filtering addresses */ ETH_BOOL promiscuous; /* promiscuous mode flag */ ETH_BOOL all_multicast; /* receive all multicast messages */ - int32 decnet_self_sent; /* loopback packets sent but not seen */ - ETH_MAC decnet_addr; /* decnet address of interface */ + ETH_BOOL hash_filter; /* filter using AUTODIN II multicast hash */ + ETH_MULTIHASH hash; /* AUTODIN II multicast hash */ + int32 loopback_self_sent; /* loopback packets sent but not seen */ + int32 loopback_self_sent_total; /* total loopback packets sent */ + int32 loopback_self_rcvd_total; /* total loopback packets seen */ + ETH_MAC physical_addr; /* physical address of interface */ + int32 have_host_nic_phy_addr; /* flag indicating that the host_nic_phy_hw_addr is valid */ + ETH_MAC host_nic_phy_hw_addr; /* MAC address of the attached NIC */ + uint32 jumbo_fragmented; /* Giant IPv4 Frames Fragmented */ + uint32 jumbo_dropped; /* Giant Frames Dropped */ DEVICE* dptr; /* device ethernet is attached to */ uint32 dbit; /* debugging bit */ int reflections; /* packet reflections on interface */ - int need_crc; /* device needs CRC (Cyclic Redundancy Check) */ + int need_crc; /* device needs CRC (Cyclic Redundancy Check) */ #if defined (USE_READER_THREAD) + int asynch_io; /* Asynchronous Interrupt scheduling enabled */ + int asynch_io_latency; /* instructions to delay pending interrupt */ ETH_QUE read_queue; pthread_mutex_t lock; pthread_t reader_thread; /* Reader Thread Id */ + pthread_t writer_thread; /* Writer Thread Id */ + pthread_mutex_t writer_lock; + pthread_cond_t writer_cond; + struct write_request { + struct write_request *next; + ETH_PACK packet; + } *write_requests; + int write_queue_peak; + struct write_request *write_buffers; + t_stat write_status; #endif }; @@ -186,18 +223,30 @@ t_stat eth_open (ETH_DEV* dev, char* name, /* open ethernet interfa t_stat eth_close (ETH_DEV* dev); /* close ethernet interface */ t_stat eth_write (ETH_DEV* dev, ETH_PACK* packet, /* write sychronous packet; */ ETH_PCALLBACK routine); /* callback when done */ -t_stat eth_read (ETH_DEV* dev, ETH_PACK* packet, /* read single packet; */ +int eth_read (ETH_DEV* dev, ETH_PACK* packet, /* read single packet; */ ETH_PCALLBACK routine); /* callback when done*/ t_stat eth_filter (ETH_DEV* dev, int addr_count, /* set filter on incoming packets */ - ETH_MAC* addresses, + ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous); +t_stat eth_filter_hash (ETH_DEV* dev, int addr_count, /* set filter on incoming packets with AUTODIN II based hash */ + ETH_MAC* const addresses, + ETH_BOOL all_multicast, + ETH_BOOL promiscuous, + ETH_MULTIHASH* const hash); +t_stat eth_check_address_conflict (ETH_DEV* dev, + ETH_MAC* const address); int eth_devices (int max, ETH_LIST* dev); /* get ethernet devices on host */ void eth_setcrc (ETH_DEV* dev, int need_crc); /* enable/disable CRC mode */ +t_stat eth_set_async (ETH_DEV* dev, int latency); /* set read behavior to be async */ +t_stat eth_clr_async (ETH_DEV* dev); /* set read behavior to be not async */ +uint32 eth_crc32(uint32 crc, const void* vbuf, size_t len); /* Compute Ethernet Autodin II CRC for buffer */ -void eth_packet_trace (ETH_DEV* dev, const uint8 *msg, int len, char* txt); /* trace ethernet packet */ +void eth_packet_trace (ETH_DEV* dev, const uint8 *msg, int len, char* txt); /* trace ethernet packet header+crc */ +void eth_packet_trace_ex (ETH_DEV* dev, const uint8 *msg, int len, char* txt, int detail, uint32 reason); /* trace ethernet packet */ t_stat eth_show (FILE* st, UNIT* uptr, /* show ethernet devices */ int32 val, void* desc); +void eth_show_dev (FILE*st, ETH_DEV* dev); /* show ethernet device state */ void eth_mac_fmt (ETH_MAC* add, char* buffer); /* format ethernet mac address */ t_stat eth_mac_scan (ETH_MAC* mac, char* strmac); /* scan string for mac, put in mac */ @@ -207,6 +256,10 @@ void ethq_clear (ETH_QUE* que); /* clear FIFO queue */ void ethq_remove (ETH_QUE* que); /* remove item from FIFO queue */ void ethq_insert (ETH_QUE* que, int32 type, /* insert item into FIFO queue */ ETH_PACK* packet, int32 status); +void ethq_insert_data(ETH_QUE* que, int32 type, /* insert item into FIFO queue */ + const uint8 *data, int used, int len, + int crc_len, const uint8 *crc_data, int32 status); +t_stat ethq_destroy(ETH_QUE* que); /* release FIFO queue */ #endif /* _SIM_ETHER_H */ From a6964f849d85ee81c4541fa04bc016314b2966b2 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Fri, 15 Apr 2011 09:04:39 -0700 Subject: [PATCH 4/5] Cleaned up parameter declarations in all simulators to achieve clean compiles on all platforms Very minor in all cases, but the strange case of swtp_cpu.c This module used expressions of the form: PC = ++PC & ADDRMASK; Officially, the C language says that expressions which modify the same variable in more than one place have undefined behavior. These were changed to the legal form which performs the desired action: PC = (PC + 1) & ADDRMASK; --- ALTAIR/altair_cpu.c | 2 +- ALTAIR/altair_sio.c | 12 ++--- AltairZ80/altairZ80_sio.c | 6 +-- AltairZ80/altairz80_hdsk.c | 4 +- AltairZ80/i8272.c | 4 +- AltairZ80/mfdc.c | 8 ++-- AltairZ80/vfdhd.c | 4 +- AltairZ80/wd179x.c | 12 ++--- Ibm1130/ibm1130_cpu.c | 12 ++--- Ibm1130/ibm1130_cr.c | 2 +- Ibm1130/ibm1130_defs.h | 4 +- Ibm1130/ibm1130_disk.c | 16 +++---- Ibm1130/ibm1130_prt.c | 2 +- Ibm1130/ibm1130_ptrp.c | 4 +- NOVA/eclipse_cpu.c | 4 +- NOVA/nova_defs.h | 4 +- NOVA/nova_sys.c | 3 +- PDP10/pdp10_cpu.c | 12 ++--- PDP11/pdp11_rx.c | 2 +- PDP11/pdp11_ry.c | 2 +- PDP11/pdp11_ts.c | 4 +- PDP11/pdp11_tu.c | 2 +- PDP18B/pdp18b_cpu.c | 2 +- PDP18B/pdp18b_fpp.c | 2 +- S3/s3_cpu.c | 10 ++--- S3/s3_defs.h | 2 +- VAX/vax780_fload.c | 2 +- VAX/vax780_sbi.c | 2 +- swtp/swtp_cpu.c | 90 +++++++++++++++++++------------------- swtp/swtp_defs.h | 2 +- swtp/swtp_dsk.c | 2 +- swtp/swtp_sio.c | 12 ++--- 32 files changed, 126 insertions(+), 125 deletions(-) diff --git a/ALTAIR/altair_cpu.c b/ALTAIR/altair_cpu.c index 740d66f7..4a837c8b 100644 --- a/ALTAIR/altair_cpu.c +++ b/ALTAIR/altair_cpu.c @@ -141,7 +141,7 @@ device addresses, if a device is plugged to a port it's routine address is here, 'nulldev' means no device is available */ struct idev { - int32 (*routine)(); + int32 (*routine)(int32, int32); }; struct idev dev_table[256] = { {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 000 */ diff --git a/ALTAIR/altair_sio.c b/ALTAIR/altair_sio.c index abe4cd9a..a60927de 100644 --- a/ALTAIR/altair_sio.c +++ b/ALTAIR/altair_sio.c @@ -122,7 +122,7 @@ DEVICE ptp_dev = { /* service routine - actually gets char & places in buffer */ -int32 sio_svc (UNIT *uptr) +t_stat sio_svc (UNIT *uptr) { int32 temp; @@ -139,12 +139,12 @@ int32 sio_svc (UNIT *uptr) } -int32 ptr_svc (UNIT *uptr) +t_stat ptr_svc (UNIT *uptr) { return SCPE_OK; } -int32 ptp_svc (UNIT *uptr) +t_stat ptp_svc (UNIT *uptr) { return SCPE_OK; } @@ -152,7 +152,7 @@ int32 ptp_svc (UNIT *uptr) /* Reset routine */ -int32 sio_reset (DEVICE *dptr) +t_stat sio_reset (DEVICE *dptr) { sio_unit.buf = 0; /* Data */ sio_unit.u3 = 0x02; /* Status */ @@ -161,7 +161,7 @@ int32 sio_reset (DEVICE *dptr) } -int32 ptr_reset (DEVICE *dptr) +t_stat ptr_reset (DEVICE *dptr) { ptr_unit.buf = 0; ptr_unit.u3 = 0x02; @@ -169,7 +169,7 @@ int32 ptr_reset (DEVICE *dptr) return SCPE_OK; } -int32 ptp_reset (DEVICE *dptr) +t_stat ptp_reset (DEVICE *dptr) { ptp_unit.buf = 0; ptp_unit.u3 = 0x02; diff --git a/AltairZ80/altairZ80_sio.c b/AltairZ80/altairZ80_sio.c index 1ae1b935..acf2c762 100644 --- a/AltairZ80/altairZ80_sio.c +++ b/AltairZ80/altairZ80_sio.c @@ -507,10 +507,10 @@ typedef struct { int32 sio_can_read; /* bit mask to indicate that one can read from this port */ int32 sio_cannot_read; /* bit mask to indicate that one cannot read from this port */ int32 sio_can_write; /* bit mask to indicate that one can write to this port */ - int32 hasReset; /* TRUE iff SIO has reset command */ + t_bool hasReset; /* TRUE iff SIO has reset command */ int32 sio_reset; /* reset command */ - int32 hasOUT; /* TRUE iff port supports OUT command */ - int32 isBuiltin; /* TRUE iff mapping is built in */ + t_bool hasOUT; /* TRUE iff port supports OUT command */ + t_bool isBuiltin; /* TRUE iff mapping is built in */ } SIO_PORT_INFO; static SIO_PORT_INFO port_table[PORT_TABLE_SIZE] = { diff --git a/AltairZ80/altairz80_hdsk.c b/AltairZ80/altairz80_hdsk.c index 009fb0e5..e90bc90e 100644 --- a/AltairZ80/altairz80_hdsk.c +++ b/AltairZ80/altairz80_hdsk.c @@ -632,8 +632,8 @@ static int32 doWrite(void) { hdskbuf[i] = GetBYTEWrapper(selectedDMA + i); rtn = sim_fwrite(hdskbuf, 1, uptr -> HDSK_SECTOR_SIZE, uptr -> fileref); if (rtn != (size_t)(uptr -> HDSK_SECTOR_SIZE)) { - TRACE_PRINT(VERBOSE_MSG, ("HDSK%d: " ADDRESS_FORMAT " Could not write Sector=%02d Track=%04d Result=%zd." NLP, - selectedDisk, PCX, selectedSector, selectedTrack, rtn)); + TRACE_PRINT(VERBOSE_MSG, ("HDSK%d: " ADDRESS_FORMAT " Could not write Sector=%02d Track=%04d Result=%d." NLP, + selectedDisk, PCX, selectedSector, selectedTrack, (int)rtn)); return CPM_ERROR; } } diff --git a/AltairZ80/i8272.c b/AltairZ80/i8272.c index 5a11ad4a..0d21de52 100644 --- a/AltairZ80/i8272.c +++ b/AltairZ80/i8272.c @@ -475,8 +475,8 @@ static char *messages[0x20] = { uint8 I8272_Write(const uint32 Addr, uint8 cData) { I8272_DRIVE_INFO *pDrive; - unsigned int flags = 0; - unsigned int readlen; + uint32 flags = 0; + uint32 readlen; uint8 disk_read = 0; int32 i; diff --git a/AltairZ80/mfdc.c b/AltairZ80/mfdc.c index bc6f3531..5ac79053 100644 --- a/AltairZ80/mfdc.c +++ b/AltairZ80/mfdc.c @@ -421,8 +421,8 @@ static uint8 MFDC_Read(const uint32 Addr) if(mfdc_info->datacount == 0) { unsigned int i, checksum; unsigned long sec_offset; - unsigned int flags; - unsigned int readlen; + uint32 flags; + uint32 readlen; /* Clear out unused portion of sector. */ memset(&sdata.u.unused[0], 0x00, 10); @@ -524,8 +524,8 @@ static uint8 MFDC_Read(const uint32 Addr) static uint8 MFDC_Write(const uint32 Addr, uint8 cData) { unsigned int sec_offset; - unsigned int flags = 0; - unsigned int writelen; + uint32 flags = 0; + uint32 writelen; MFDC_DRIVE_INFO *pDrive; pDrive = &mfdc_info->drive[mfdc_info->sel_drive]; diff --git a/AltairZ80/vfdhd.c b/AltairZ80/vfdhd.c index df021b87..11db5b54 100644 --- a/AltairZ80/vfdhd.c +++ b/AltairZ80/vfdhd.c @@ -547,7 +547,7 @@ static void VFDHD_Command(void) if(vfdhd_info->read == 1) { /* Perform a Read operation */ unsigned int i, checksum; - unsigned int readlen; + uint32 readlen; TRACE_PRINT(RD_DATA_MSG, ("VFDHD: " ADDRESS_FORMAT " RD: Drive=%d, Track=%d, Head=%d, Sector=%d" NLP, PCX, @@ -619,7 +619,7 @@ static void VFDHD_Command(void) } } else { /* Perform a Write operation */ - unsigned int writelen; + uint32 writelen; TRACE_PRINT(WR_DATA_MSG, ("VFDHD: " ADDRESS_FORMAT " WR: Drive=%d, Track=%d, Head=%d, Sector=%d" NLP, PCX, diff --git a/AltairZ80/wd179x.c b/AltairZ80/wd179x.c index e0f77454..743e779a 100644 --- a/AltairZ80/wd179x.c +++ b/AltairZ80/wd179x.c @@ -445,8 +445,8 @@ uint8 WD179X_Read(const uint32 Addr) { uint8 cData; WD179X_DRIVE_INFO *pDrive; - unsigned int flags = 0; - unsigned int readlen; + uint32 flags = 0; + uint32 readlen; int status; if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) { @@ -578,8 +578,8 @@ static uint8 Do1793Command(uint8 cCommand) { uint8 result = 0; WD179X_DRIVE_INFO *pDrive; - unsigned int flags = 0; - unsigned int readlen; + uint32 flags = 0; + uint32 readlen; int status; if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) { @@ -955,8 +955,8 @@ uint8 WD179X_Write(const uint32 Addr, uint8 cData) { WD179X_DRIVE_INFO *pDrive; /* uint8 disk_read = 0; */ - unsigned int flags = 0; - unsigned int writelen; + uint32 flags = 0; + uint32 writelen; if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) { return 0xFF; diff --git a/Ibm1130/ibm1130_cpu.c b/Ibm1130/ibm1130_cpu.c index 1db5600f..d35bbcbd 100644 --- a/Ibm1130/ibm1130_cpu.c +++ b/Ibm1130/ibm1130_cpu.c @@ -197,7 +197,7 @@ int32 int_mask; /* current active interrupt mask (ipl sensitive) */ int32 mem_mask; /* mask for memory address bits based on current memory size */ int32 cpu_dsw = 0; /* CPU device status word */ int32 ibkpt_addr = -1; /* breakpoint addr */ -int32 sim_gui = TRUE; /* enable gui */ +t_bool sim_gui = TRUE; /* enable gui */ t_bool running = FALSE; /* TRUE if CPU is running */ t_bool power = TRUE; /* TRUE if CPU power is on */ t_bool cgi = FALSE; /* TRUE if we are running as a CGI program */ @@ -222,7 +222,7 @@ t_stat cpu_set_type (UNIT *uptr, int32 value, char *cptr, void *desc); void calc_ints (void); extern t_stat ts_wr (int32 data, int32 addr, int32 access); -extern t_stat detach_cmd (int flags, char *cptr); +extern t_stat detach_cmd (int32 flags, char *cptr); extern UNIT cr_unit; extern int32 sim_switches; @@ -230,7 +230,7 @@ extern int32 sim_switches; static void archive_backtrace(char *inst); static void reset_backtrace (void); static void show_backtrace (int nshow); - static t_stat backtrace_cmd (int flag, char *cptr); + static t_stat backtrace_cmd (int32 flag, char *cptr); #else #define archive_backtrace(inst) #define reset_backtrace() @@ -245,7 +245,7 @@ extern int32 sim_switches; static void init_console_window (void); static void destroy_console_window (void); -static t_stat view_cmd (int flag, char *cptr); +static t_stat view_cmd (int32 flag, char *cptr); static t_stat cpu_attach (UNIT *uptr, char *cptr); static t_bool bsctest (int32 DSPLC, t_bool reset_V); static void exit_irq (void); @@ -1556,7 +1556,7 @@ static void show_backtrace (int nshow) putchar('\n'); } -static t_stat backtrace_cmd (int flag, char *cptr) +static t_stat backtrace_cmd (int32 flag, char *cptr) { int n; @@ -1854,7 +1854,7 @@ void debug_print (char *fmt, ...) /* view_cmd - let user view and/or edit a file (e.g. a printer output file, script, or source deck) */ -static t_stat view_cmd (int flag, char *cptr) +static t_stat view_cmd (int32 flag, char *cptr) { #ifdef _WIN32 char cmdline[256]; diff --git a/Ibm1130/ibm1130_cr.c b/Ibm1130/ibm1130_cr.c index 61014b71..c45205a8 100644 --- a/Ibm1130/ibm1130_cr.c +++ b/Ibm1130/ibm1130_cr.c @@ -951,7 +951,7 @@ t_stat load_cr_boot (int drvno, int switches) return SCPE_OK; } -t_stat cr_boot (int unitno, DEVICE *dptr) +t_stat cr_boot (int32 unitno, DEVICE *dptr) { t_stat rval; int i; diff --git a/Ibm1130/ibm1130_defs.h b/Ibm1130/ibm1130_defs.h index 36963a6a..d4f925b3 100644 --- a/Ibm1130/ibm1130_defs.h +++ b/Ibm1130/ibm1130_defs.h @@ -47,7 +47,7 @@ extern int cgi; /* TRUE if we are running as a CGI program */ extern int cgiwritable; /* TRUE if we can write the disk images back to the image file in CGI mode */ -extern int sim_gui; +extern t_bool sim_gui; extern uint16 M[]; /* core memory, up to 32Kwords (note: don't even think about trying 64K) */ extern uint16 ILSW[]; /* interrupt level status words */ @@ -269,7 +269,7 @@ void xio_error (char *msg); void bail (char *msg); t_stat load_cr_boot (int drv, int switches); -t_stat cr_boot (int unitno, DEVICE *dptr); +t_stat cr_boot (int32 unitno, DEVICE *dptr); t_stat cr_rewind (void); t_stat cr_detach (UNIT *uptr); void calc_ints (void); /* recalculate interrupt bitmask */ diff --git a/Ibm1130/ibm1130_disk.c b/Ibm1130/ibm1130_disk.c index 52dc8525..4b776470 100644 --- a/Ibm1130/ibm1130_disk.c +++ b/Ibm1130/ibm1130_disk.c @@ -43,9 +43,9 @@ extern int32 sim_switches; extern int32 sim_quiet; static int trace_dms = 0; static void tracesector (int iswrite, int nwords, int addr, int sector); -static t_stat where_cmd (int flag, char *ptr); -static t_stat phdebug_cmd (int flag, char *ptr); -static t_stat fdump_cmd (int flags, char *cptr); +static t_stat where_cmd (int32 flag, char *ptr); +static t_stat phdebug_cmd (int32 flag, char *ptr); +static t_stat fdump_cmd (int32 flags, char *cptr); static void enable_dms_tracing (int newsetting); #endif @@ -91,7 +91,7 @@ static t_stat dsk_svc (UNIT *uptr); static t_stat dsk_reset (DEVICE *dptr); static t_stat dsk_attach (UNIT *uptr, char *cptr); static t_stat dsk_detach (UNIT *uptr); -static t_stat dsk_boot (int unitno, DEVICE *dptr); +static t_stat dsk_boot (int32 unitno, DEVICE *dptr); static void diskfail (UNIT *uptr, int dswflag, int unitflag, t_bool do_interrupt); @@ -571,7 +571,7 @@ static t_stat dsk_detach (UNIT *uptr) /* boot routine - if they type BOOT DSK, load the standard boot card. */ -static t_stat dsk_boot (int unitno, DEVICE *dptr) +static t_stat dsk_boot (int32 unitno, DEVICE *dptr) { t_stat rval; @@ -644,7 +644,7 @@ char * saywhere (int addr) static int phdebug_lo = -1, phdebug_hi = -1; -static t_stat phdebug_cmd (int flag, char *ptr) +static t_stat phdebug_cmd (int32 flag, char *ptr) { int val1, val2; @@ -671,7 +671,7 @@ static t_stat phdebug_cmd (int flag, char *ptr) return SCPE_OK; } -static t_stat where_cmd (int flag, char *ptr) +static t_stat where_cmd (int32 flag, char *ptr) { int addr; char *where; @@ -836,7 +836,7 @@ done: savesector(addr, offset, nwords, phid, name); } -static t_stat fdump_cmd (int flags, char *cptr) +static t_stat fdump_cmd (int32 flags, char *cptr) { int addr = 0x7a24; /* address of next statement */ int sofst = 0x7a26, symaddr; diff --git a/Ibm1130/ibm1130_prt.c b/Ibm1130/ibm1130_prt.c index 30fe258c..90103b8f 100644 --- a/Ibm1130/ibm1130_prt.c +++ b/Ibm1130/ibm1130_prt.c @@ -637,7 +637,7 @@ static t_stat prt1403_svc(UNIT *uptr) /* delete_cmd - SCP command to delete a file */ -static t_stat delete_cmd (int flag, char *cptr) +static t_stat delete_cmd (int32 flag, char *cptr) { char gbuf[CBUFSIZE]; int status; diff --git a/Ibm1130/ibm1130_ptrp.c b/Ibm1130/ibm1130_ptrp.c index 71498e3c..c24a27d4 100644 --- a/Ibm1130/ibm1130_ptrp.c +++ b/Ibm1130/ibm1130_ptrp.c @@ -37,7 +37,7 @@ static t_stat ptr_svc (UNIT *uptr); static t_stat ptr_reset (DEVICE *dptr); static t_stat ptr_attach (UNIT *uptr, char *cptr); static t_stat ptr_detach (UNIT *uptr); -static t_stat ptr_boot (int unitno, DEVICE *dptr); +static t_stat ptr_boot (int32 unitno, DEVICE *dptr); static t_stat ptp_svc (UNIT *uptr); static t_stat ptp_reset (DEVICE *dptr); static t_stat ptp_attach (UNIT *uptr, char *cptr); @@ -228,7 +228,7 @@ static t_stat ptr_detach (UNIT *uptr) /* ptr_attach - perform paper tape initial program load */ -static t_stat ptr_boot (int unitno, DEVICE *dptr) +static t_stat ptr_boot (int32 unitno, DEVICE *dptr) { int ch, nch, val, addr; t_bool leader = TRUE, start = FALSE; diff --git a/NOVA/eclipse_cpu.c b/NOVA/eclipse_cpu.c index 7802a1a8..50f0bfcf 100644 --- a/NOVA/eclipse_cpu.c +++ b/NOVA/eclipse_cpu.c @@ -5957,12 +5957,12 @@ int32 Debug_Entry(int32 PC, int32 inst, int32 inst2, int32 AC0, int32 AC1, int32 return 0; } -int32 Debug_Dump(UNIT *uptr, int32 val, char *cptr, void *desc) +t_stat Debug_Dump(UNIT *uptr, int32 val, char *cptr, void *desc) { return SCPE_OK; } -int32 Dump_History (FILE *st, UNIT *uptr, int32 val, void *desc) +t_stat Dump_History (FILE *st, UNIT *uptr, int32 val, void *desc) { char debmap[4], debion[4]; t_value simeval[20]; diff --git a/NOVA/nova_defs.h b/NOVA/nova_defs.h index b5edbd36..cc802f73 100644 --- a/NOVA/nova_defs.h +++ b/NOVA/nova_defs.h @@ -223,14 +223,14 @@ struct ndev { int32 mask; /* done/busy mask */ int32 pi; /* assigned pi bit */ - int32 (*routine)(); /* dispatch routine */ + int32 (*routine)(int32, int32, int32); /* dispatch routine */ }; typedef struct { int32 dnum; /* device number */ int32 mask; /* done/busy mask */ int32 pi; /* assigned pi bit */ - int32 (*routine)(); /* dispatch routine */ + int32 (*routine)(int32, int32, int32); /* dispatch routine */ } DIB; /* Device flags (simulator representation) diff --git a/NOVA/nova_sys.c b/NOVA/nova_sys.c index 0dab680b..4b066f54 100644 --- a/NOVA/nova_sys.c +++ b/NOVA/nova_sys.c @@ -861,7 +861,8 @@ return SCPE_ARG; char *get_addr (char *cptr, t_addr addr, t_bool ext, int32 cflag, int32 *val) { -int32 d, r, x, pflag; +int32 d, x, pflag; +t_stat r; char gbuf[CBUFSIZE]; int32 dmax, dsign; diff --git a/PDP10/pdp10_cpu.c b/PDP10/pdp10_cpu.c index 652341d8..b73e88c0 100644 --- a/PDP10/pdp10_cpu.c +++ b/PDP10/pdp10_cpu.c @@ -446,24 +446,24 @@ const d10 bytemask[64] = { 0, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES }; -static t_bool (*io700d[16])() = { +static t_bool (*io700d[16])(a10, int32) = { &aprid, NULL, NULL, NULL, &wrapr, &rdapr, &czapr, &coapr, NULL, NULL, NULL, NULL, &wrpi, &rdpi, &czpi, &copi }; -static t_bool (*io701d[16])() = { +static t_bool (*io701d[16])(a10, int32) = { NULL, &rdubr, &clrpt, &wrubr, &wrebr, &rdebr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -static t_bool (*io702d[16])() = { +static t_bool (*io702d[16])(a10, int32) = { &rdspb, &rdcsb, &rdpur, &rdcstm, &rdtim, &rdint, &rdhsb, NULL, &wrspb, &wrcsb, &wrpur, &wrcstm, &wrtim, &wrint, &wrhsb, NULL }; #define io700i io700d -static t_bool (*io701i[16])() = { +static t_bool (*io701i[16])(a10, int32) = { &clrcsh, &rdubr, &clrpt, &wrubr, &wrebr, &rdebr, NULL, NULL, NULL, &rdpcst, NULL, &wrpcst, NULL, NULL, NULL, NULL }; -static t_bool (*io702i[16])() = { +static t_bool (*io702i[16])(a10, int32) = { &sdbr1, &sdbr2, &sdbr3, &sdbr4, &rdtim, &rdint, &rdhsb, &spm, &ldbr1, &ldbr2, &ldbr3, &ldbr4, &wrtim, &wrint, &wrhsb, &lpmr }; @@ -697,7 +697,7 @@ for ( ;; ) { /* loop until ABORT */ int32 op, ac, i, st, xr, xct_cnt, its_2pr, pflgs; a10 ea; d10 inst, mb, indrct, rs[2]; -t_bool (*fptr)(); +t_bool (*fptr)(int32, int32); pager_PC = PC; /* update pager PC */ pager_tc = FALSE; /* not in trap cycle */ diff --git a/PDP11/pdp11_rx.c b/PDP11/pdp11_rx.c index 73d60a39..bba47ec4 100644 --- a/PDP11/pdp11_rx.c +++ b/PDP11/pdp11_rx.c @@ -130,7 +130,7 @@ t_stat rx_wr (int32 data, int32 PA, int32 access); t_stat rx_svc (UNIT *uptr); t_stat rx_reset (DEVICE *dptr); t_stat rx_boot (int32 unitno, DEVICE *dptr); -void rx_done (int esr_flags, int new_ecode); +void rx_done (int32 esr_flags, int32 new_ecode); /* RX11 data structures diff --git a/PDP11/pdp11_ry.c b/PDP11/pdp11_ry.c index 23a86595..5d69ae0f 100644 --- a/PDP11/pdp11_ry.c +++ b/PDP11/pdp11_ry.c @@ -161,7 +161,7 @@ t_stat ry_wr (int32 data, int32 PA, int32 access); t_stat ry_svc (UNIT *uptr); t_stat ry_reset (DEVICE *dptr); t_stat ry_boot (int32 unitno, DEVICE *dptr); -void ry_done (int esr_flags, int new_ecode); +void ry_done (int32 esr_flags, int32 new_ecode); t_stat ry_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat ry_attach (UNIT *uptr, char *cptr); diff --git a/PDP11/pdp11_ts.c b/PDP11/pdp11_ts.c index 6aae9e28..cf75d14b 100644 --- a/PDP11/pdp11_ts.c +++ b/PDP11/pdp11_ts.c @@ -735,7 +735,7 @@ fnc = GET_FNC (cmdhdr); /* get fnc+mode */ mod = GET_MOD (cmdhdr); if (DEBUG_PRS (ts_dev)) fprintf (sim_deb, ">>TS: cmd=%s, mod=%o, buf=%o, lnt=%d, pos=%d\n", - fnc_name[fnc], mod, cmdadl, cmdlnt, ts_unit.pos); + fnc_name[fnc], mod, cmdadl, cmdlnt, (int)ts_unit.pos); if ((fnc != FNC_WCHR) && (tssr & TSSR_NBA)) { /* ~wr chr & nba? */ ts_endcmd (TC3, 0, 0); /* error */ return SCPE_OK; @@ -1018,7 +1018,7 @@ if (cmdhdr & CMD_IE) ts_ownm = 0; ts_ownc = 0; if (DEBUG_PRS (ts_dev)) fprintf (sim_deb, ">>TS: sta=%o, tc=%o, rfc=%d, pos=%d\n", - msgxs0, GET_TC (tssr), msgrfc, ts_unit.pos); + msgxs0, GET_TC (tssr), msgrfc, (int)ts_unit.pos); return; } diff --git a/PDP11/pdp11_tu.c b/PDP11/pdp11_tu.c index 91d86198..f933a7c0 100644 --- a/PDP11/pdp11_tu.c +++ b/PDP11/pdp11_tu.c @@ -598,7 +598,7 @@ return MBE_GOE; /* Abort transfer */ -t_stat tu_abort (void) +int32 tu_abort (void) { return tu_reset (&tu_dev); } diff --git a/PDP18B/pdp18b_cpu.c b/PDP18B/pdp18b_cpu.c index cfce154c..9545390e 100644 --- a/PDP18B/pdp18b_cpu.c +++ b/PDP18B/pdp18b_cpu.c @@ -1989,7 +1989,7 @@ else *ea = (PC & BLKMASK) | (t & IAMASK); /* within 32K */ return sta; } -t_stat Incr_addr (int32 ma) +int32 Incr_addr (int32 ma) { if (memm) return ((ma & B_EPCMASK) | ((ma + 1) & B_DAMASK)); diff --git a/PDP18B/pdp18b_fpp.c b/PDP18B/pdp18b_fpp.c index becbbe24..1a22c4c2 100644 --- a/PDP18B/pdp18b_fpp.c +++ b/PDP18B/pdp18b_fpp.c @@ -159,7 +159,7 @@ t_stat fp15_fmul (int32 ir, UFP *a, UFP *b); t_stat fp15_fdiv (int32 ir, UFP *a, UFP *b); t_stat fp15_fix (int32 ir, UFP *a); t_stat fp15_norm (int32 ir, UFP *a, UFP *b, t_bool rnd); -t_stat fp15_exc (int32 sta); +t_stat fp15_exc (t_stat sta); void fp15_asign (int32 ir, UFP *a); void dp_add (UFP *a, UFP *b); void dp_sub (UFP *a, UFP *b); diff --git a/S3/s3_cpu.c b/S3/s3_cpu.c index 4655773f..0574d15b 100644 --- a/S3/s3_cpu.c +++ b/S3/s3_cpu.c @@ -398,13 +398,13 @@ extern t_stat sim_activate (UNIT *uptr, int32 delay); extern int32 fprint_sym (FILE *of, int32 addr, uint32 *val, UNIT *uptr, int32 sw); int32 nulldev (int32 opcode, int32 m, int32 n, int32 data); -int add_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2); +int32 add_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2); int32 subtract_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2); static int32 compare(int32 byte1, int32 byte2, int32 cond); static int32 condition(int32 qbyte); static void store_decimal (int32 addr, int32 len, uint8 *dec, int sign); -static void load_decimal (int32 addr, int32 len, uint8 *result, int32 *count, int32 *sign); -static void add_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int32 *count); +static void load_decimal (int32 addr, int32 len, uint8 *result, int *count, int *sign); +static void add_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *count); static void subtract_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *count, int *sign); int32 GetMem(int32 addr); int32 PutMem(int32 addr, int32 data); @@ -1541,7 +1541,7 @@ int sign1, sign2, sign3; /* Sign of operands & result */ /* This field is set to zero if the result is all zero, */ /* or to MAX_DECIMAL_DIGITS+1 if overflow occurred. */ /*-------------------------------------------------------------------*/ -static void add_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int32 *count) +static void add_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *count) { int d; /* Decimal digit */ int i; /* Array subscript */ @@ -1684,7 +1684,7 @@ uint8 *lower; /* -> Lower value operand */ /* exception, or if the operand causes a data exception */ /* because of invalid decimal digits or sign. */ /*-------------------------------------------------------------------*/ -static void load_decimal (int32 addr, int32 len, uint8 *result, int32 *count, int32 *sign) +static void load_decimal (int32 addr, int32 len, uint8 *result, int *count, int *sign) { int h; /* Hexadecimal digit */ int i, j; /* Array subscripts */ diff --git a/S3/s3_defs.h b/S3/s3_defs.h index b218e509..3bb0a93d 100644 --- a/S3/s3_defs.h +++ b/S3/s3_defs.h @@ -67,7 +67,7 @@ struct ndev { int32 level; /* interrupt level */ int32 pri; /* Device priority */ - int32 (*routine)(); /* dispatch routine */ + int32 (*routine)(int32, int32, int32, int32); /* dispatch routine */ }; /* Structure to define operation codes */ diff --git a/VAX/vax780_fload.c b/VAX/vax780_fload.c index d8c2ed3e..b4f94c08 100644 --- a/VAX/vax780_fload.c +++ b/VAX/vax780_fload.c @@ -88,7 +88,7 @@ uint32 rtfile_find (uint32 block, uint32 sector); /* FLOAD file_name {file_origin} */ -t_stat vax780_fload (int flag, char *cptr) +t_stat vax780_fload (int32 flag, char *cptr) { char gbuf[CBUFSIZE]; uint16 file_name[3], blkbuf[BLK_SIZE]; diff --git a/VAX/vax780_sbi.c b/VAX/vax780_sbi.c index afcb6914..5f707041 100644 --- a/VAX/vax780_sbi.c +++ b/VAX/vax780_sbi.c @@ -138,7 +138,7 @@ void sbi_set_tmo (int32 pa); void uba_eval_int (void); t_stat vax780_boot (int32 flag, char *ptr); -extern t_stat vax780_fload (int flag, char *cptr); +extern t_stat vax780_fload (int32 flag, char *cptr); extern int32 intexc (int32 vec, int32 cc, int32 ipl, int ei); extern int32 iccs_rd (void); extern int32 nicr_rd (void); diff --git a/swtp/swtp_cpu.c b/swtp/swtp_cpu.c index 09cdaa83..e01f1d7e 100644 --- a/swtp/swtp_cpu.c +++ b/swtp/swtp_cpu.c @@ -161,7 +161,7 @@ address is here, 'nulldev' means no device is available */ struct idev { - int32 (*routine)(); + int32 (*routine)(int32, int32); }; struct idev dev_table[32] = { @@ -405,7 +405,7 @@ int32 sim_instr (void) } */ IR = OP = mem_get_byte(PC); /* fetch instruction */ - PC = ++PC & ADDRMASK; /* increment PC */ + PC = (PC + 1) & ADDRMASK; /* increment PC */ sim_interval--; /* The Big Instruction Decode Switch */ @@ -421,11 +421,11 @@ int32 sim_instr (void) A = get_psw(); break; case 0x08: /* INX */ - IX = ++IX & ADDRMASK; + IX = (IX + 1) & ADDRMASK; condevalZ(IX); break; case 0x09: /* DEX */ - IX = --IX & ADDRMASK; + IX = (IX + 1) & ADDRMASK; condevalZ(IX); break; case 0x0A: /* CLV */ @@ -558,60 +558,60 @@ int32 sim_instr (void) IX = (SP + 1) & ADDRMASK; break; case 0x31: /* INS */ - SP = ++SP & ADDRMASK; + SP = (SP + 1) & ADDRMASK; break; case 0x32: /* PUL A */ - SP = ++SP & ADDRMASK; + SP = (SP + 1) & ADDRMASK; A = mem_get_byte(SP); break; case 0x33: /* PUL B */ - SP = ++SP & ADDRMASK; + SP = (SP + 1) & ADDRMASK; B = mem_get_byte(SP); break; case 0x34: /* DES */ - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; break; case 0x35: /* TXS */ SP = (IX - 1) & ADDRMASK; break; case 0x36: /* PSH A */ mem_put_byte(SP, A); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; break; case 0x37: /* PSH B */ mem_put_byte(SP, B); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; break; case 0x39: /* RTS */ - SP = ++SP & ADDRMASK; + SP = (SP + 1) & ADDRMASK; PC = mem_get_word(SP) & ADDRMASK; - SP = ++SP & ADDRMASK; + SP = (SP + 1) & ADDRMASK; break; case 0x3B: /* RTI */ - SP = ++SP & ADDRMASK; + SP = (SP + 1) & ADDRMASK; set_psw(mem_get_byte(SP)); - SP = ++SP & ADDRMASK; + SP = (SP + 1) & ADDRMASK; B = mem_get_byte(SP); - SP = ++SP & ADDRMASK; + SP = (SP + 1) & ADDRMASK; A = mem_get_byte(SP); - SP = ++SP & ADDRMASK; + SP = (SP + 1) & ADDRMASK; IX = mem_get_word(SP); - SP = (SP += 2) & ADDRMASK; + SP = (SP + 2) & ADDRMASK; PC = mem_get_word(SP) & ADDRMASK; - SP = ++SP & ADDRMASK; + SP = (SP + 1) & ADDRMASK; break; case 0x3E: /* WAI */ - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; mem_put_word(SP, PC); - SP = (SP -= 2) & ADDRMASK; + SP = (SP - 2) & ADDRMASK; mem_put_word(SP, IX); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; mem_put_byte(SP, A); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; mem_put_byte(SP, B); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; mem_put_byte(SP, get_psw()); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; if (I) { reason = STOP_HALT; continue; @@ -621,17 +621,17 @@ int32 sim_instr (void) } break; case 0x3F: /* SWI */ - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; mem_put_word(SP, PC); - SP = (SP -= 2) & ADDRMASK; + SP = (SP - 2) & ADDRMASK; mem_put_word(SP, IX); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; mem_put_byte(SP, A); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; mem_put_byte(SP, B); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; mem_put_byte(SP, get_psw()); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; I = 0x10000; PC = mem_get_word(0xFFFB) & ADDRMASK; break; @@ -720,7 +720,7 @@ int32 sim_instr (void) V = 0; if (A == 0x80) V = 0x10000; - A = --A & 0xFF; + A = (A - 1) & 0xFF; condevalN(A); condevalZ(A); break; @@ -728,7 +728,7 @@ int32 sim_instr (void) V = 0; if (A == 0x7F) V = 0x10000; - A = ++A & 0xFF; + A = (A + 1) & 0xFF; condevalN(A); condevalZ(A); break; @@ -829,7 +829,7 @@ int32 sim_instr (void) V = 0; if (B == 0x80) V = 0x10000; - B = --B & 0xFF; + B = (B - 1) & 0xFF; condevalN(B); condevalZ(B); break; @@ -837,7 +837,7 @@ int32 sim_instr (void) V = 0; if (B == 0x7F) V = 0x10000; - B = ++B & 0xFF; + B = (B + 1) & 0xFF; condevalN(B); condevalZ(B); break; @@ -957,7 +957,7 @@ int32 sim_instr (void) V = 0; if (lo == 0x80) V = 0x10000; - lo = --lo & 0xFF; + lo = (lo - 1) & 0xFF; mem_put_byte(DAR, lo); condevalN(lo); condevalZ(lo); @@ -968,7 +968,7 @@ int32 sim_instr (void) V = 0; if (lo == 0x7F) V = 0x10000; - lo = ++lo & 0xFF; + lo = (lo + 1) & 0xFF; mem_put_byte(DAR, lo); condevalN(lo); condevalZ(lo); @@ -1094,7 +1094,7 @@ int32 sim_instr (void) V = 0; if (lo == 0x80) V = 0x10000; - lo = --lo & 0xFF; + lo = (lo - 1) & 0xFF; mem_put_byte(DAR, lo); condevalN(lo); condevalZ(lo); @@ -1105,7 +1105,7 @@ int32 sim_instr (void) V = 0; if (lo == 0x7F) V = 0x10000; - lo = ++lo & 0xFF; + lo = (lo + 1) & 0xFF; mem_put_byte(DAR, lo); condevalN(lo); condevalZ(lo); @@ -1215,9 +1215,9 @@ int32 sim_instr (void) break; case 0x8D: /* BSR rel */ lo = get_rel_addr(); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; mem_put_word(SP, PC); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; PC = PC + lo; PC &= ADDRMASK; break; @@ -1429,9 +1429,9 @@ int32 sim_instr (void) break; case 0xAD: /* JSR ind */ DAR = get_indir_addr(); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; mem_put_word(SP, PC); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; PC = DAR; break; case 0xAE: /* LDS ind */ @@ -1542,9 +1542,9 @@ int32 sim_instr (void) break; case 0xBD: /* JSR ext */ DAR = get_ext_addr(); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; mem_put_word(SP, PC); - SP = --SP & ADDRMASK; + SP = (SP - 1) & ADDRMASK; PC = DAR; break; case 0xBE: /* LDS ext */ @@ -2010,7 +2010,7 @@ int32 get_dir_addr() int32 temp; temp = mem_get_byte(PC); - PC = ++PC & ADDRMASK; + PC = (PC + 1) & ADDRMASK; return temp & 0xFF; } diff --git a/swtp/swtp_defs.h b/swtp/swtp_defs.h index 9e946c18..cf9041ad 100644 --- a/swtp/swtp_defs.h +++ b/swtp/swtp_defs.h @@ -27,7 +27,7 @@ Copyright (c) 2005, 2007, William Beech */ -#include "../sim_defs.h" // simulator defs +#include "sim_defs.h" // simulator defs /* Memory */ diff --git a/swtp/swtp_dsk.c b/swtp/swtp_dsk.c index 3a636279..b41943ef 100644 --- a/swtp/swtp_dsk.c +++ b/swtp/swtp_dsk.c @@ -408,7 +408,7 @@ int32 fdccmd(int32 io, int32 data) #endif break; default: - printf("Unknown FDC command %02H\n\r", data); + printf("Unknown FDC command %02X\n\r", data); } } else { /* read status from fdc */ val = cur_flg[cur_dsk]; /* set return value */ diff --git a/swtp/swtp_sio.c b/swtp/swtp_sio.c index 859074c4..1f6f8a77 100644 --- a/swtp/swtp_sio.c +++ b/swtp/swtp_sio.c @@ -114,7 +114,7 @@ int32 ptp_flag = 0, ptr_flag = 0; /* console input service routine */ -int32 sio_svc (UNIT *uptr) +t_stat sio_svc (UNIT *uptr) { int32 temp; @@ -132,21 +132,21 @@ int32 sio_svc (UNIT *uptr) /* paper tape reader input service routine */ -int32 ptr_svc (UNIT *uptr) +t_stat ptr_svc (UNIT *uptr) { return SCPE_OK; } /* paper tape punch output service routine */ -int32 ptp_svc (UNIT *uptr) +t_stat ptp_svc (UNIT *uptr) { return SCPE_OK; } /* Reset console */ -int32 sio_reset (DEVICE *dptr) +t_stat sio_reset (DEVICE *dptr) { sio_unit.buf = 0; // Data buffer sio_unit.u3 = 0x02; // Status buffer @@ -156,7 +156,7 @@ int32 sio_reset (DEVICE *dptr) /* Reset paper tape reader */ -int32 ptr_reset (DEVICE *dptr) +t_stat ptr_reset (DEVICE *dptr) { ptr_unit.buf = 0; ptr_unit.u3 = 0x02; @@ -166,7 +166,7 @@ int32 ptr_reset (DEVICE *dptr) /* Reset paper tape punch */ -int32 ptp_reset (DEVICE *dptr) +t_stat ptp_reset (DEVICE *dptr) { ptp_unit.buf = 0; ptp_unit.u3 = 0x02; From 4ca8935d60e862488ea7c6c67d317a9bf4408947 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Fri, 15 Apr 2011 09:11:32 -0700 Subject: [PATCH 5/5] Revised VMS build (descrip.mms) and makefile Revised VMS build to include new simulators and resolve other build issues Added AsyncIO support Fixed support for clean operation on VAX, AXP and IA64 and various VMS versions. Cleaned up makefile to properly detect pcap and readline and cleanly build on all supported platforms which use the makefile: X86 (mix of 32bit & 64bit) gcc: Linux NetBSD OpenBSD FreeBSD Solaris OSX Sparc(big endian) gcc: Linux Solaris --- descrip.mms | 294 +++++++++++++++++++++++++++++++++++++--------------- makefile | 150 +++++++++++++++++++++++---- 2 files changed, 341 insertions(+), 103 deletions(-) diff --git a/descrip.mms b/descrip.mms index 1dd77ed7..4982b9b0 100644 --- a/descrip.mms +++ b/descrip.mms @@ -2,13 +2,13 @@ # Written By: Robert Alan Byer / byer@mail.ourservers.net # Modified By: Mark Pizzolato / mark@infocomm.com # Norman Lastovica / norman.lastovica@oracle.com -# Camiel Vanderhoeven / camiel@camicom.com +# Camiel Vanderhoeven / camiel@camicom.com # # This MMS/MMK build script is used to compile the various simulators in # the SIMH package for OpenVMS using DEC C v6.0-001(AXP), v6.5-001(AXP), -# HP C V7.2-001 (IA64) and v6.4-005(VAX). +# HP C V7.3-009-48GBT (AXP), HP C V7.2-001 (IA64) and v6.4-005(VAX). # -# Notes: On VAX, the PDP-10 and Eclipse simulators will not be built +# Notes: On VAX, the PDP-10and IBM 7094 simulators will not be built # due to the fact that INT64 is required for that simulator. # # This build script will accept the following build options. @@ -23,6 +23,7 @@ # HP2100 Just Build The Hewlett-Packard HP-2100. # I1401 Just Build The IBM 1401. # I1620 Just Build The IBM 1620. +# I7094 Just Build The IBM 7094. # IBM1130 Just Build The IBM 1130. # ID16 Just Build The Interdata 16-bit CPU. # ID32 Just Build The Interdata 32-bit CPU. @@ -37,6 +38,7 @@ # PDP15 Just Build The DEC PDP-15. # S3 Just Build The IBM System 3. # SDS Just Build The SDS 940. +# SWTP Just Build The SWTP. # VAX Just Build The DEC VAX. # VAX780 Just Build The DEC VAX780. # CLEAN Will Clean Files Back To Base Kit. @@ -46,6 +48,11 @@ # # MMK/MACRO=(DEBUG=1) # +# To build on older Alpha VMS platforms, SIM_ASYNCH_IO must be disabled. +# use.. +# +# MMK/MACRO=(NOASYNCH=1) +# # This will produce an executable named {Simulator}-{I64|VAX|AXP}-DBG.EXE # @@ -62,19 +69,23 @@ CC_OPTIMIZE = /NOOPTIMIZE .IFDEF MMSALPHA ALPHA_OR_IA64 = 1 CC_FLAGS = /PREF=ALL -ARCH = AXP-DBG +.IFDEF NOASYNCH +ARCH = AXP-NOASYNCH-DBG CC_DEFS = "_LARGEFILE" +.ELSE +ARCH = AXP-DBG +CC_DEFS = "_LARGEFILE","SIM_ASYNCH_IO=1" +.ENDIF .ENDIF .IFDEF MMSIA64 ALPHA_OR_IA64 = 1 CC_FLAGS = /PREF=ALL ARCH = I64-DBG -CC_DEFS = "_LARGEFILE" +CC_DEFS = "_LARGEFILE","SIM_ASYNCH_IO=1" .ENDIF .IFDEF MMSVAX -ALPHA_OR_IA64 = 0 CC_FLAGS = $(CC_FLAGS) ARCH = VAX-DBG CC_DEFS = "__VAX" @@ -87,8 +98,13 @@ LINK_DEBUG = /NODEBUG/NOTRACEBACK ALPHA_OR_IA64 = 1 CC_OPTIMIZE = /OPT=(LEV=5)/ARCH=HOST CC_FLAGS = /PREF=ALL -ARCH = AXP +.IFDEF NOASYNCH +ARCH = AXP-NOASYNCH CC_DEFS = "_LARGEFILE" +.ELSE +ARCH = AXP +CC_DEFS = "_LARGEFILE","SIM_ASYNCH_IO=1" +.ENDIF LINK_SECTION_BINDING = /SECTION_BINDING .ENDIF @@ -97,11 +113,10 @@ ALPHA_OR_IA64 = 1 CC_OPTIMIZE = /OPT=(LEV=5) CC_FLAGS = /PREF=ALL ARCH = I64 -CC_DEFS = "_LARGEFILE" +CC_DEFS = "_LARGEFILE","SIM_ASYNCH_IO=1" .ENDIF .IFDEF MMSVAX -ALPHA_OR_IA64 = 0 CC_OPTIMIZE = /OPTIMIZE CC_FLAGS = $(CC_FLAGS) ARCH = VAX @@ -120,15 +135,18 @@ CC = CC/DECC$(OUR_CC_FLAGS) # Define The platform specific Build Directory Where The Objects Will Go. # BIN_DIR = SYS$DISK:[.BIN] -LIB_DIR = SYS$DISK:[.LIB] -BLD_DIR = SYS$DISK:[.LIB.BLD-$(ARCH)] +LIB_DIR = SYS$DISK:[.BIN.VMS.LIB] +BLD_DIR = SYS$DISK:[.BIN.VMS.LIB.BLD-$(ARCH)] # Check To Make Sure We Have SYS$DISK:[.BIN] & SYS$DISK:[.LIB] Directory. # .FIRST + @ IF ((F$GETSYI("ARCH_NAME").EQS."Alpha").AND.(F$GETSYI("VERSION").LES."V8.0").AND.("$(NOASYNCH)".EQS."")) THEN WRITE SYS$OUTPUT "*** WARNING **** Build should be invoked with /MACRO=NOASYNCH=1 on this platform" + @ IF ((F$GETSYI("ARCH_NAME").EQS."Alpha").AND.(F$GETSYI("VERSION").LES."V8.0").AND.("$(NOASYNCH)".EQS."")) THEN EXIT %x10000000 @ IF (F$SEARCH("SYS$DISK:[]BIN.DIR").EQS."") THEN CREATE/DIRECTORY $(BIN_DIR) - @ IF (F$SEARCH("SYS$DISK:[]LIB.DIR").EQS."") THEN CREATE/DIRECTORY $(LIB_DIR) - @ IF (F$SEARCH("SYS$DISK:[.LIB]BLD-$(ARCH).DIR").EQS."") THEN CREATE/DIRECTORY $(BLD_DIR) + @ IF (F$SEARCH("SYS$DISK:[.BIN]VMS.DIR").EQS."") THEN CREATE/DIRECTORY $(LIB_DIR) + @ IF (F$SEARCH("SYS$DISK:[.BIN.VMS]LIB.DIR").EQS."") THEN CREATE/DIRECTORY $(LIB_DIR) + @ IF (F$SEARCH("SYS$DISK:[.BIN.VMS.LIB]BLD-$(ARCH).DIR").EQS."") THEN CREATE/DIRECTORY $(BLD_DIR) @ IF (F$SEARCH("$(BLD_DIR)*.*").NES."") THEN DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.*;* @ IF "".NES."''CC'" THEN DELETE/SYMBOL/GLOBAL CC @@ -139,7 +157,11 @@ SIMH_LIB = $(LIB_DIR)SIMH-$(ARCH).OLB SIMH_SOURCE = $(SIMH_DIR)SIM_CONSOLE.C,$(SIMH_DIR)SIM_SOCK.C,\ $(SIMH_DIR)SIM_TMXR.C,$(SIMH_DIR)SIM_ETHER.C,\ $(SIMH_DIR)SIM_TAPE.C,$(SIMH_DIR)SIM_FIO.C,\ - $(SIMH_DIR)SIM_TIMER.C + $(SIMH_DIR)SIM_TIMER.C,$(SIMH_DIR)SIM_DISK.C +SIMH_MAIN = SCP.C +.IFDEF ALPHA_OR_IA64 +SIMH_LIB64 = $(LIB_DIR)SIMH64-$(ARCH).OLB +.ENDIF # VMS PCAP File Definitions. # @@ -227,7 +249,7 @@ ECLIPSE_SOURCE = $(NOVA_DIR)ECLIPSE_CPU.C,$(NOVA_DIR)ECLIPSE_TT.C,\ $(NOVA_DIR)NOVA_PT.C,$(NOVA_DIR)NOVA_CLK.C,\ $(NOVA_DIR)NOVA_TT1.C,$(NOVA_DIR)NOVA_QTY.C ECLIPSE_OPTIONS = /INCL=($(SIMH_DIR),$(NOVA_DIR))\ - /DEF=($(CC_DEFS),"USE_INT64=1","ECLIPSE=1") + /DEF=($(CC_DEFS),"ECLIPSE=1") # # GRI Corporation GRI-909 Simulator Definitions. @@ -275,7 +297,7 @@ HP2100_SOURCE1 = $(HP2100_DIR)HP2100_STDDEV.C,$(HP2100_DIR)HP2100_DP.C,\ HP2100_LIB2 = $(LIB_DIR)HP2100L2-$(ARCH).OLB HP2100_SOURCE2 = $(HP2100_DIR)HP2100_FP1.C,$(HP2100_DIR)HP2100_BACI.C,\ $(HP2100_DIR)HP2100_MPX.C,$(HP2100_DIR)HP2100_PIF.C -.IF ALPHA_OR_IA64 +.IFDEF ALPHA_OR_IA64 HP2100_OPTIONS = /INCL=($(SIMH_DIR),$(HP2100_DIR))\ /DEF=($(CC_DEFS),"HAVE_INT64=1") .ELSE @@ -453,6 +475,15 @@ SDS_SOURCE = $(SDS_DIR)SDS_CPU.C,$(SDS_DIR)SDS_DRM.C,$(SDS_DIR)SDS_DSK.C,\ $(SDS_DIR)SDS_SYS.C SDS_OPTIONS = /INCL=($(SIMH_DIR),$(SDS_DIR))/DEF=($(CC_DEFS)) +# +# SWTP 6800 +# +SWTP_DIR = SYS$DISK:[.SWTP] +SWTP_LIB = $(LIB_DIR)SWTP-$(ARCH).OLB +SWTP_SOURCE = $(SWTP_DIR)SWTP_CPU.C,$(SWTP_DIR)SWTP_DSK.C,$(SWTP_DIR)SWTP_SIO.C,\ + $(SWTP_DIR)SWTP_SYS.C +SWTP_OPTIONS = /INCL=($(SIMH_DIR),$(SWTP_DIR))/DEF=($(CC_DEFS)) + # # Digital Equipment VAX Simulator Definitions. # @@ -470,8 +501,15 @@ VAX_SOURCE = $(VAX_DIR)VAX_CIS.C,$(VAX_DIR)VAX_CMODE.C,\ $(PDP11_DIR)PDP11_LP.C,$(PDP11_DIR)PDP11_TQ.C,\ $(PDP11_DIR)PDP11_XQ.C,$(PDP11_DIR)PDP11_CR.C,\ $(PDP11_DIR)PDP11_RY.C,$(PDP11_DIR)PDP11_VH.C +.IFDEF ALPHA_OR_IA64 +VAX_OPTIONS = /INCL=($(SIMH_DIR),$(VAX_DIR),$(PDP11_DIR)$(PCAP_INC))\ + /DEF=($(CC_DEFS),"VM_VAX=1","USE_ADDR64=1","USE_INT64=1"$(PCAP_DEFS)) +VAX_SIMH_LIB = $(SIMH_LIB64) +.ELSE VAX_OPTIONS = /INCL=($(SIMH_DIR),$(VAX_DIR),$(PDP11_DIR)$(PCAP_INC))\ /DEF=($(CC_DEFS),"VM_VAX=1"$(PCAP_DEFS)) +VAX_SIMH_LIB = $(SIMH_LIB) +.ENDIF # Digital Equipment VAX780 Simulator Definitions. # @@ -493,8 +531,15 @@ VAX780_SOURCE2 = $(PDP11_DIR)PDP11_RL.C,$(PDP11_DIR)PDP11_RQ.C,\ $(PDP11_DIR)PDP11_CR.C,$(PDP11_DIR)PDP11_RP.C,\ $(PDP11_DIR)PDP11_TU.C,$(PDP11_DIR)PDP11_HK.C,\ $(PDP11_DIR)PDP11_IO_LIB.C +.IFDEF ALPHA_OR_IA64 +VAX780_OPTIONS = /INCL=($(SIMH_DIR),$(VAX780_DIR),$(PDP11_DIR)$(PCAP_INC))\ + /DEF=($(CC_DEFS),"VM_VAX=1","USE_ADDR64=1","USE_INT64=1"$(PCAP_DEFS),"VAX_780=1") +VAX780_SIMH_LIB = $(SIMH_LIB64) +.ELSE VAX780_OPTIONS = /INCL=($(SIMH_DIR),$(VAX780_DIR),$(PDP11_DIR)$(PCAP_INC))\ /DEF=($(CC_DEFS),"VM_VAX=1"$(PCAP_DEFS),"VAX_780=1") +VAX780_SIMH_LIB = $(SIMH_LIB) +.ENDIF # IBM 7094 Simulator Definitions. # @@ -510,17 +555,17 @@ I7094_OPTIONS = /INCL=($(SIMH_DIR),$(I7094_DIR))/DEF=($(CC_DEFS)) # If we're not a VAX, Build Everything # -.IF ALPHA_OR_IA64 +.IFDEF ALPHA_OR_IA64 ALL : ALTAIR ALTAIRZ80 ECLIPSE GRI LGP H316 HP2100 I1401 I1620 IBM1130 ID16 \ ID32 NOVA PDP1 PDP4 PDP7 PDP8 PDP9 PDP10 PDP11 PDP15 S3 VAX VAX780 SDS \ - I7094 + I7094 SWTP $! No further actions necessary .ELSE # # Else We Are On VAX And Build Everything EXCEPT the 64b simulators # -ALL : ALTAIR ALTAIRZ80 GRI H316 HP2100 I1401 I1620 IBM1130 ID16 ID32 \ - NOVA PDP1 PDP4 PDP7 PDP8 PDP9 PDP11 PDP15 S3 VAX VAX780 SDS +ALL : ALTAIR ALTAIRZ80 ECLIPSE GRI H316 HP2100 I1401 I1620 IBM1130 ID16 ID32 \ + NOVA PDP1 PDP4 PDP7 PDP8 PDP9 PDP11 PDP15 S3 VAX VAX780 SDS SWTP $! No further actions necessary .ENDIF @@ -553,6 +598,19 @@ $(SIMH_LIB) : $(SIMH_SOURCE) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* +.IFDEF ALPHA_OR_IA64 +$(SIMH_LIB64) : $(SIMH_SOURCE) + $! + $! Building The $(SIMH_LIB64) Library. + $! + $ $(CC)/DEF=($(CC_DEFS)$(PCAP_DEFS),"USE_ADDR64=1","USE_INT64=1")$(PCAP_SIMH_INC) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* +.ENDIF + $(ALTAIR_LIB) : $(ALTAIR_SOURCE) $! $! Building The $(ALTAIR_LIB) Library. @@ -586,10 +644,6 @@ $(ALTAIRZ80_LIB2) : $(ALTAIRZ80_SOURCE2) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -# -# If Not On VAX, Build The Eclipse Library. -# -.IF ALPHA_OR_IA64 $(ECLIPSE_LIB) : $(ECLIPSE_SOURCE) $! $! Building The $(ECLIPSE_LIB) Library. @@ -600,16 +654,6 @@ $(ECLIPSE_LIB) : $(ECLIPSE_SOURCE) LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -.ELSE -# -# We Are On VAX And Due To The Use of INT64 We Can't Build It. -# -$(ECLIPSE_LIB) : - $! - $! Due To The Use Of INT64 We Can't Build The - $! $(LIB_DIR)ECLIPSE-$(ARCH).OLB Library On VAX. - $! -.ENDIF $(GRI_LIB) : $(GRI_SOURCE) $! @@ -790,7 +834,7 @@ $(PDP9_LIB) : $(PDP18B_SOURCE) # # If Not On VAX, Build The PDP-10 Library. # -.IF ALPHA_OR_IA64 +.IFDEF ALPHA_OR_IA64 $(PDP10_LIB) : $(PDP10_SOURCE) $! $! Building The $(PDP10_LIB) Library. @@ -807,7 +851,7 @@ $(PDP10_LIB) : $(PDP10_SOURCE) # $(PDP10_LIB) : $! Due To The Use Of INT64 We Can't Build The - $! $(LIB_DIR)PDP10-$(ARCH).OLB Library On VAX. + $! $(MMS$TARGET) Library On VAX. .ENDIF $(PDP11_LIB1) : $(PDP11_SOURCE1) @@ -865,6 +909,17 @@ $(SDS_LIB) : $(SDS_SOURCE) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* +$(SWTP_LIB) : $(SWTP_SOURCE) + $! + $! Building The $(SWTP_LIB) Library. + $! + $ $(CC)$(SWTP_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + $(VAX_LIB) : $(VAX_SOURCE) $! $! Building The $(VAX_LIB) Library. @@ -913,7 +968,7 @@ $(PCAP_LIB) : $(PCAP_SOURCE) # # If Not On VAX, Build The IBM 7094 Library. # -.IF ALPHA_OR_IA64 +.IFDEF ALPHA_OR_IA64 $(I7094_LIB) : $(I7094_SOURCE) $! $! Building The $(I7094_LIB) Library. @@ -930,13 +985,16 @@ $(I7094_LIB) : $(I7094_SOURCE) # $(I7094_LIB) : $! Due To The Use Of INT64 We Can't Build The - $! $(LIB_DIR)I7094-$(ARCH).OLB Library On VAX. + $! $(MMS$TARGET) Library On VAX. .ENDIF # # Individual Simulator Builds. # -ALTAIR : $(SIMH_LIB) $(ALTAIR_LIB) +ALTAIR : $(BIN_DIR)ALTAIR-$(ARCH).EXE + $! ALTAIR done + +$(BIN_DIR)ALTAIR-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(ALTAIR_LIB) $! $! Building The $(BIN_DIR)ALTAIR-$(ARCH).EXE Simulator. $! @@ -945,7 +1003,10 @@ ALTAIR : $(SIMH_LIB) $(ALTAIR_LIB) $(BLD_DIR)SCP.OBJ,$(ALTAIR_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -ALTAIRZ80 : $(SIMH_LIB) $(ALTAIRZ80_LIB1) $(ALTAIRZ80_LIB2) +ALTAIRZ80 : $(BIN_DIR)ALTAIRZ80-$(ARCH).EXE + $! ALTAIRZ80 done + +$(BIN_DIR)ALTAIRZ80-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(ALTAIRZ80_LIB1) $(ALTAIRZ80_LIB2) $! $! Building The $(BIN_DIR)ALTAIRZ80-$(ARCH).EXE Simulator. $! @@ -955,11 +1016,10 @@ ALTAIRZ80 : $(SIMH_LIB) $(ALTAIRZ80_LIB1) $(ALTAIRZ80_LIB2) $(ALTAIRZ80_LIB2)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -# -# If Not On VAX, Build The PDP-10 Simulator. -# -.IF ALPHA_OR_IA64 -ECLIPSE : $(SIMH_LIB) $(ECLIPSE_LIB) +ECLIPSE : $(BIN_DIR)ECLIPSE-$(ARCH).EXE + $! ECLIPSE done + +$(BIN_DIR)ECLIPSE-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(ECLIPSE_LIB) $! $! Building The $(BIN_DIR)ECLIPSE-$(ARCH).EXE Simulator. $! @@ -967,17 +1027,11 @@ ECLIPSE : $(SIMH_LIB) $(ECLIPSE_LIB) $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)ECLIPSE-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(ECLIPSE_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -.ELSE -# -# Else We Are On VAX And Tell The User We Can't Build On VAX -# Due To The Use Of INT64. -# -ECLIPSE : - $! Sorry, Can't Build $(BIN_DIR)ECLIPSE-$(ARCH).EXE Simulator - $! Because It Requires The Use Of INT64. -.ENDIF -GRI : $(SIMH_LIB) $(GRI_LIB) +GRI : $(BIN_DIR)GRI-$(ARCH).EXE + $! GRI done + +$(BIN_DIR)GRI-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(GRI_LIB) $! $! Building The $(BIN_DIR)GRI-$(ARCH).EXE Simulator. $! @@ -986,7 +1040,10 @@ GRI : $(SIMH_LIB) $(GRI_LIB) $(BLD_DIR)SCP.OBJ,$(GRI_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -LGP : $(SIMH_LIB) $(LGP_LIB) +LGP : $(BIN_DIR)LGP-$(ARCH).EXE + $! LGP done + +$(BIN_DIR)LGP-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(LGP_LIB) $! $! Building The $(BIN_DIR)LGP-$(ARCH).EXE Simulator. $! @@ -995,7 +1052,10 @@ LGP : $(SIMH_LIB) $(LGP_LIB) $(BLD_DIR)SCP.OBJ,$(LGP_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -H316 : $(SIMH_LIB) $(H316_LIB) +H316 : $(BIN_DIR)H316-$(ARCH).EXE + $! H316 done + +$(BIN_DIR)H316-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(H316_LIB) $! $! Building The $(BIN_DIR)H316-$(ARCH).EXE Simulator. $! @@ -1004,7 +1064,10 @@ H316 : $(SIMH_LIB) $(H316_LIB) $(BLD_DIR)SCP.OBJ,$(H316_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -HP2100 : $(SIMH_LIB) $(HP2100_LIB1) $(HP2100_LIB2) +HP2100 : $(BIN_DIR)HP2100-$(ARCH).EXE + $! HP2100 done + +$(BIN_DIR)HP2100-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(HP2100_LIB1) $(HP2100_LIB2) $! $! Building The $(BIN_DIR)HP2100-$(ARCH).EXE Simulator. $! @@ -1014,7 +1077,10 @@ HP2100 : $(SIMH_LIB) $(HP2100_LIB1) $(HP2100_LIB2) $(HP2100_LIB2)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -I1401 : $(SIMH_LIB) $(I1401_LIB) +I1401 : $(BIN_DIR)I1401-$(ARCH).EXE + $! I1401 done + +$(BIN_DIR)I1401-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(I1401_LIB) $! $! Building The $(BIN_DIR)I1401-$(ARCH).EXE Simulator. $! @@ -1023,7 +1089,10 @@ I1401 : $(SIMH_LIB) $(I1401_LIB) $(BLD_DIR)SCP.OBJ,$(I1401_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -I1620 : $(SIMH_LIB) $(I1620_LIB) +I1620 : $(BIN_DIR)I1620-$(ARCH).EXE + $! I1620 done + +$(BIN_DIR)I1620-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(I1620_LIB) $! $! Building The $(BIN_DIR)I1620-$(ARCH).EXE Simulator. $! @@ -1032,7 +1101,10 @@ I1620 : $(SIMH_LIB) $(I1620_LIB) $(BLD_DIR)SCP.OBJ,$(I1620_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -IBM1130 : $(SIMH_LIB) $(IBM1130_LIB) +IBM1130 : $(BIN_DIR)IBM1130-$(ARCH).EXE + $! IBM1130 done + +$(BIN_DIR)IBM1130-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(IBM1130_LIB) $! $! Building The $(BIN_DIR)IBM1130-$(ARCH).EXE Simulator. $! @@ -1041,7 +1113,10 @@ IBM1130 : $(SIMH_LIB) $(IBM1130_LIB) $(BLD_DIR)SCP.OBJ,$(IBM1130_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -ID16 : $(SIMH_LIB) $(ID16_LIB) +ID16 : $(BIN_DIR)ID16-$(ARCH).EXE + $! ID16 done + +$(BIN_DIR)ID16-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(ID16_LIB) $! $! Building The $(BIN_DIR)ID16-$(ARCH).EXE Simulator. $! @@ -1050,7 +1125,10 @@ ID16 : $(SIMH_LIB) $(ID16_LIB) $(BLD_DIR)SCP.OBJ,$(ID16_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -ID32 : $(SIMH_LIB) $(ID32_LIB) +ID32 : $(BIN_DIR)ID32-$(ARCH).EXE + $! ID32 done + +$(BIN_DIR)ID32-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(ID32_LIB) $! $! Building The $(BIN_DIR)ID32-$(ARCH).EXE Simulator. $! @@ -1059,7 +1137,10 @@ ID32 : $(SIMH_LIB) $(ID32_LIB) $(BLD_DIR)SCP.OBJ,$(ID32_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -NOVA : $(SIMH_LIB) $(NOVA_LIB) +NOVA : $(BIN_DIR)NOVA-$(ARCH).EXE + $! NOVA done + +$(BIN_DIR)NOVA-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(NOVA_LIB) $! $! Building The $(BIN_DIR)NOVA-$(ARCH).EXE Simulator. $! @@ -1068,7 +1149,10 @@ NOVA : $(SIMH_LIB) $(NOVA_LIB) $(BLD_DIR)SCP.OBJ,$(NOVA_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -PDP1 : $(SIMH_LIB) $(PDP1_LIB) +PDP1 : $(BIN_DIR)PDP1-$(ARCH).EXE + $! PDP1 done + +$(BIN_DIR)PDP1-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(PDP1_LIB) $! $! Building The $(BIN_DIR)PDP1-$(ARCH).EXE Simulator. $! @@ -1077,7 +1161,10 @@ PDP1 : $(SIMH_LIB) $(PDP1_LIB) $(BLD_DIR)SCP.OBJ,$(PDP1_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -PDP4 : $(SIMH_LIB) $(PDP4_LIB) +PDP4 : $(BIN_DIR)PDP4-$(ARCH).EXE + $! PDP4 done + +$(BIN_DIR)PDP4-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(PDP4_LIB) $! $! Building The $(BIN_DIR)PDP4-$(ARCH).EXE Simulator. $! @@ -1086,7 +1173,10 @@ PDP4 : $(SIMH_LIB) $(PDP4_LIB) $(BLD_DIR)SCP.OBJ,$(PDP4_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -PDP7 : $(SIMH_LIB) $(PDP7_LIB) +PDP7 : $(BIN_DIR)PDP7-$(ARCH).EXE + $! PDP7 done + +$(BIN_DIR)PDP7-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(PDP7_LIB) $! $! Building The $(BIN_DIR)PDP7-$(ARCH).EXE Simulator. $! @@ -1095,7 +1185,10 @@ PDP7 : $(SIMH_LIB) $(PDP7_LIB) $(BLD_DIR)SCP.OBJ,$(PDP7_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -PDP8 : $(SIMH_LIB) $(PDP8_LIB) +PDP8 : $(BIN_DIR)PDP8-$(ARCH).EXE + $! PDP8 done + +$(BIN_DIR)PDP8-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(PDP8_LIB) $! $! Building The $(BIN_DIR)PDP8-$(ARCH).EXE Simulator. $! @@ -1104,7 +1197,10 @@ PDP8 : $(SIMH_LIB) $(PDP8_LIB) $(BLD_DIR)SCP.OBJ,$(PDP8_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -PDP9 : $(SIMH_LIB) $(PDP9_LIB) +PDP9 : $(BIN_DIR)PDP9-$(ARCH).EXE + $! PDP9 done + +$(BIN_DIR)PDP9-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(PDP9_LIB) $! $! Building The $(BIN_DIR)PDP9-$(ARCH).EXE Simulator. $! @@ -1116,8 +1212,11 @@ PDP9 : $(SIMH_LIB) $(PDP9_LIB) # # If Not On VAX, Build The PDP-10 Simulator. # -.IF ALPHA_OR_IA64 -PDP10 : $(SIMH_LIB) $(PCAP_LIBD) $(PDP10_LIB) $(PCAP_EXECLET) +.IFDEF ALPHA_OR_IA64 +PDP10 : $(BIN_DIR)PDP10-$(ARCH).EXE + $! PDP10 done + +$(BIN_DIR)PDP10-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(PCAP_LIBD) $(PDP10_LIB) $(PCAP_EXECLET) $! $! Building The $(BIN_DIR)PDP10-$(ARCH).EXE Simulator. $! @@ -1135,7 +1234,10 @@ PDP10 : $! Because It Requires The Use Of INT64. .ENDIF -PDP11 : $(SIMH_LIB) $(PCAP_LIBD) $(PDP11_LIB1) $(PDP11_LIB2) $(PCAP_EXECLET) +PDP11 : $(BIN_DIR)PDP11-$(ARCH).EXE + $! PDP11 done + +$(BIN_DIR)PDP11-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(PCAP_LIBD) $(PDP11_LIB1) $(PDP11_LIB2) $(PCAP_EXECLET) $! $! Building The $(BIN_DIR)PDP11-$(ARCH).EXE Simulator. $! @@ -1144,7 +1246,10 @@ PDP11 : $(SIMH_LIB) $(PCAP_LIBD) $(PDP11_LIB1) $(PDP11_LIB2) $(PCAP_EXECLET) $(BLD_DIR)SCP.OBJ,$(PDP11_LIB1)/LIBRARY,$(PDP11_LIB2)/LIBRARY,$(SIMH_LIB)/LIBRARY$(PCAP_LIBR) $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -PDP15 : $(SIMH_LIB) $(PDP15_LIB) +PDP15 : $(BIN_DIR)PDP15-$(ARCH).EXE + $! PDP15 done + +$(BIN_DIR)PDP15-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(PDP15_LIB) $! $! Building The $(BIN_DIR)PDP15-$(ARCH).EXE Simulator. $! @@ -1153,7 +1258,10 @@ PDP15 : $(SIMH_LIB) $(PDP15_LIB) $(BLD_DIR)SCP.OBJ,$(PDP15_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -S3 : $(SIMH_LIB) $(S3_LIB) +S3 : $(BIN_DIR)S3-$(ARCH).EXE + $! S3 done + +$(BIN_DIR)S3-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(S3_LIB) $! $! Building The $(BIN_DIR)S3-$(ARCH).EXE Simulator. $! @@ -1162,7 +1270,10 @@ S3 : $(SIMH_LIB) $(S3_LIB) $(BLD_DIR)SCP.OBJ,$(S3_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -SDS : $(SIMH_LIB) $(SDS_LIB) +SDS : $(BIN_DIR)SDS-$(ARCH).EXE + $! SDS done + +$(BIN_DIR)SDS-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(SDS_LIB) $! $! Building The $(BIN_DIR)SDS-$(ARCH).EXE Simulator. $! @@ -1171,7 +1282,22 @@ SDS : $(SIMH_LIB) $(SDS_LIB) $(BLD_DIR)SCP.OBJ,$(SDS_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -VAX : $(SIMH_LIB) $(PCAP_LIBD) $(VAX_LIB) $(PCAP_EXECLET) +SWTP : $(BIN_DIR)SWTP-$(ARCH).EXE + $! SWTP done + +$(BIN_DIR)SWTP-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(SWTP_LIB) + $! + $! Building The $(BIN_DIR)SWTP-$(ARCH).EXE Simulator. + $! + $ $(CC)$(SWTP_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)SWTP-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(SWTP_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +VAX : $(BIN_DIR)VAX-$(ARCH).EXE + $! VAX done + +$(BIN_DIR)VAX-$(ARCH).EXE : $(SIMH_MAIN) $(VAX_SIMH_LIB) $(PCAP_LIBD) $(VAX_LIB) $(PCAP_EXECLET) $! $! Building The $(BIN_DIR)VAX-$(ARCH).EXE Simulator. $! @@ -1179,10 +1305,13 @@ VAX : $(SIMH_LIB) $(PCAP_LIBD) $(VAX_LIB) $(PCAP_EXECLET) $ LINK $(LINK_DEBUG)$(LINK_SECTION_BINDING)- /EXE=$(BIN_DIR)VAX-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(VAX_LIB)/LIBRARY,- - $(SIMH_LIB)/LIBRARY$(PCAP_LIBR) + $(VAX_SIMH_LIB)/LIBRARY$(PCAP_LIBR) $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* -VAX780 : $(SIMH_LIB) $(PCAP_LIBD) $(VAX780_LIB1) $(VAX780_LIB2) $(PCAP_EXECLET) +VAX780 : $(BIN_DIR)VAX780-$(ARCH).EXE + $! VAX780 done + +$(BIN_DIR)VAX780-$(ARCH).EXE : $(SIMH_MAIN) $(VAX780_SIMH_LIB) $(PCAP_LIBD) $(VAX780_LIB1) $(VAX780_LIB2) $(PCAP_EXECLET) $! $! Building The $(BIN_DIR)VAX780-$(ARCH).EXE Simulator. $! @@ -1191,14 +1320,17 @@ VAX780 : $(SIMH_LIB) $(PCAP_LIBD) $(VAX780_LIB1) $(VAX780_LIB2) $(PCAP_EXECLET) /EXE=$(BIN_DIR)VAX780-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,- $(VAX780_LIB1)/LIBRARY,$(VAX780_LIB2)/LIBRARY,- - $(SIMH_LIB)/LIBRARY$(PCAP_LIBR) + $(VAX780_SIMH_LIB)/LIBRARY$(PCAP_LIBR) $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* # # If Not On VAX, Build The IBM 7094 Simulator. # -.IF ALPHA_OR_IA64 -I7094 : $(SIMH_LIB) $(I7094_LIB) +.IFDEF ALPHA_OR_IA64 +I7094 : $(BIN_DIR)I7094-$(ARCH).EXE + $! I7094 done + +$(BIN_DIR)I7094-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_LIB) $(I7094_LIB) $! $! Building The $(BIN_DIR)I7094-$(ARCH).EXE Simulator. $! diff --git a/makefile b/makefile index 57cde601..296f4a38 100644 --- a/makefile +++ b/makefile @@ -1,39 +1,117 @@ # -# CC Command +# This GMU make makefile has been tested on: +# Linux (x86 & Sparc) +# OS X +# Solaris (x86 & Sparc) +# OpenBSD +# NetBSD +# FreeBSD +# Windows (MinGW & cygwin) +# +# +# CC Command (and platform available options). (Poor man's autoconf) # ifeq ($(WIN32),) - #Unix Environments - ifneq (,$(findstring solaris,$(OSTYPE))) - OS_CCDEFS = -lm -lsocket -lnsl -lrt -lpthread -D_GNU_SOURCE + #*nix Environments (&& cygwin) + ifeq (SunOS,$(shell uname)) + TEST = /bin/test else - ifneq (,$(findstring darwin,$(OSTYPE))) - OS_CCDEFS = -D_GNU_SOURCE + TEST = test + endif + ifeq (Darwin,$(shell uname)) + LIBEXT = dylib + else + LIBEXT = a + endif + OS_CCDEFS = -D_GNU_SOURCE + ifeq (libm,$(shell if $(TEST) -e /usr/lib/libm.$(LIBEXT); then echo libm; fi)) + OS_LDFLAGS += -lm + endif + ifeq (SunOS,$(shell uname)) + OS_CCDEFS += -I/opt/sfw/include + OS_LDFLAGS += -lsocket -lnsl -lrt -lm -lpthread -L/opt/sfw/lib -R/opt/sfw/lib + endif + ifeq (cygwin,$(findstring cygwin,$(OSTYPE))) + OS_CCDEFS += -O2 + endif + ifeq (librt,$(shell if $(TEST) -e /usr/lib/librt.$(LIBEXT); then echo librt; fi)) + OS_LDFLAGS += -lrt + endif + ifeq (libpthread,$(shell if $(TEST) -e /usr/lib/libpthread.$(LIBEXT); then echo libpthread; fi)) + OS_CCDEFS += -DSIM_ASYNCH_IO -DUSE_READER_THREAD + OS_LDFLAGS += -lpthread + endif + ifeq (readline,$(shell if $(TEST) -e /usr/lib/libreadline.$(LIBEXT) -o -e /opt/sfw/lib/libreadline.a; then echo readline; fi)) + # Use Locally installed and available readline support + ifeq (ncurses,$(shell if $(TEST) -e /usr/lib/libncurses.$(LIBEXT) -o -e /opt/sfw/lib/libncurses.a; then echo ncurses; fi)) + OS_CCDEFS += -DHAVE_READLINE + OS_LDFLAGS += -lreadline -lncurses else - OS_CCDEFS = -lrt -lm -D_GNU_SOURCE + OS_CCDEFS += -DHAVE_READLINE + OS_LDFLAGS += -lreadline endif endif - CC = gcc -std=c99 -U__STRICT_ANSI__ -g $(OS_CCDEFS) -I . - ifeq ($(USE_NETWORK),) - else + ifeq (pcap,$(shell if $(TEST) -e /usr/include/pcap.h -o -e /opt/sfw/include/pcap.h; then echo pcap; fi)) + # Use Locally installed and available pcap support + NETWORK_CCDEFS = -DUSE_NETWORK + NETWORK_LDFLAGS = -lpcap + endif + ifeq (tuntap,$(shell if $(TEST) -e /usr/include/linux/if_tun.h; then echo tuntap; fi)) + # Provide support for Tap networking on Linux + NETWORK_TAP_CCDEFS = -DUSE_TAP_NETWORK + endif + ifeq (bsdtuntap,$(shell if $(TEST) -e /usr/include/net/if_tun.h; then echo bsdtuntap; fi)) + # Provide support for Tap networking + NETWORK_TAP_CCDEFS = -DUSE_TAP_NETWORK -DUSE_BSDTUNTAP + endif + ifneq (binexists,$(shell if $(TEST) -e BIN; then echo binexists; fi)) + MKDIRBIN = if $(TEST) ! -e BIN; then mkdir BIN; fi + endif + ifneq ($(USE_NETWORK),) + # Assume built from tcpdump.org sources with default install target NETWORK_OPT = -DUSE_NETWORK -isystem /usr/local/include /usr/local/lib/libpcap.a endif else - #Win32 Environments - LDFLAGS = -lm -lwsock32 -lwinmm - CC = gcc -std=c99 -U__STRICT_ANSI__ -O2 -I. - EXE = .exe - ifeq ($(USE_NETWORK),) + #Win32 Environments (via MinGW32) + GCC_Path := $(dir $(shell where gcc.exe)) + ifeq (pthreads,$(shell if exist ..\pthreads\Pre-built.2\include\pthread.h echo pthreads)) + PTHREADS_CCDEFS = -DSIM_ASYNCH_IO -DUSE_READER_THREAD -I../pthreads/Pre-built.2/include + PTHREADS_LDFLAGS = -lpthreadVC2 -L..\pthreads\Pre-built.2\lib else - NETWORK_OPT = -DUSE_NETWORK -lwpcap -lpacket + ifeq (pthreads,$(shell if exist $(dir $(GCC_Path))..\include\pthread.h echo pthreads)) + PTHREADS_CCDEFS = -DSIM_ASYNCH_IO -DUSE_READER_THREAD + PTHREADS_LDFLAGS = -lpthreads + endif + endif + ifeq (pcap,$(shell if exist ..\winpcap\Wpdpack\include\pcap.h echo pcap)) + PCAP_CCDEFS = -I../winpcap/Wpdpack/include -DUSE_SHARED + NETWORK_LDFLAGS = + else + ifeq (pcap,$(shell if exist $(dir $(GCC_Path))..\include\pcap.h echo pcap)) + PCAP_CCDEFS = -DUSE_SHARED + NETWORK_LDFLAGS = + endif + endif + OS_CCDEFS = -fms-extensions -O2 $(PTHREADS_CCDEFS) $(PCAP_CCDEFS) + OS_LDFLAGS = -lm -lwsock32 -lwinmm $(PTHREADS_LDFLAGS) + EXE = .exe + ifneq (binexists,$(shell if exist BIN echo binexists)) + MKDIRBIN = if not exist BIN mkdir BIN + endif + ifneq ($(USE_NETWORK),) + NETWORK_OPT = -DUSE_SHARED endif endif +CC = gcc -std=c99 -U__STRICT_ANSI__ -g -I . $(NETWORK_CCDEFS) $(NETWORK_TAP_CCDEFS) $(OS_CCDEFS) +LDFLAGS = $(OS_LDFLAGS) $(NETWORK_LDFLAGS) + # # Common Libraries # BIN = BIN/ SIM = scp.c sim_console.c sim_fio.c sim_timer.c sim_sock.c \ - sim_tmxr.c sim_ether.c sim_tape.c + sim_tmxr.c sim_ether.c sim_tape.c sim_disk.c # @@ -58,7 +136,7 @@ ECLIPSE = ${NOVAD}/eclipse_cpu.c ${NOVAD}/eclipse_tt.c ${NOVAD}/nova_sys.c \ ${NOVAD}/nova_dkp.c ${NOVAD}/nova_dsk.c ${NOVAD}/nova_lp.c \ ${NOVAD}/nova_mta.c ${NOVAD}/nova_plt.c ${NOVAD}/nova_pt.c \ ${NOVAD}/nova_clk.c ${NOVAD}/nova_tt1.c ${NOVAD}/nova_qty.c -ECLIPSE_OPT = -I ${NOVAD} -DECLIPSE -DUSE_INT64 +ECLIPSE_OPT = -I ${NOVAD} -DECLIPSE PDP18BD = PDP18B @@ -135,7 +213,7 @@ PDP8 = ${PDP8D}/pdp8_cpu.c ${PDP8D}/pdp8_clk.c ${PDP8D}/pdp8_df.c \ PDP8_OPT = -I ${PDP8D} -H316D = H316 +H316D = h316 H316 = ${H316D}/h316_stddev.c ${H316D}/h316_lp.c ${H316D}/h316_cpu.c \ ${H316D}/h316_sys.c ${H316D}/h316_mt.c ${H316D}/h316_fhd.c \ ${H316D}/h316_dp.c @@ -251,7 +329,7 @@ SDS = ${SDSD}/sds_cpu.c ${SDSD}/sds_drm.c ${SDSD}/sds_dsk.c ${SDSD}/sds_io.c \ ${SDSD}/sds_stddev.c ${SDSD}/sds_sys.c SDS_OPT = -I ${SDSD} -SWTPD = SWTP +SWTPD = swtp SWTP = ${SWTPD}/swtp_cpu.c ${SWTPD}/swtp_dsk.c ${SWTPD}/swtp_sio.c \ ${SWTPD}/swtp_sys.c SWTP_OPT = -I ${SWTPD} @@ -269,9 +347,10 @@ all : ${ALL} clean : ifeq ($(WIN32),) - ${RM} ${BIN}* + ${RM} -r ${BIN} else if exist BIN\*.exe del /q BIN\*.exe + if exist BIN rmdir BIN endif # @@ -280,134 +359,161 @@ endif pdp1 : ${BIN}pdp1${EXE} ${BIN}pdp1${EXE} : ${PDP1} ${SIM} + ${MKDIRBIN} ${CC} ${PDP1} ${SIM} ${PDP1_OPT} -o $@ ${LDFLAGS} pdp4 : ${BIN}pdp4${EXE} ${BIN}pdp4${EXE} : ${PDP18B} ${SIM} + ${MKDIRBIN} ${CC} ${PDP18B} ${SIM} ${PDP4_OPT} -o $@ ${LDFLAGS} pdp7 : ${BIN}pdp7${EXE} ${BIN}pdp7${EXE} : ${PDP18B} ${SIM} + ${MKDIRBIN} ${CC} ${PDP18B} ${SIM} ${PDP7_OPT} -o $@ ${LDFLAGS} pdp8 : ${BIN}pdp8${EXE} ${BIN}pdp8${EXE} : ${PDP8} ${SIM} + ${MKDIRBIN} ${CC} ${PDP8} ${SIM} ${PDP8_OPT} -o $@ ${LDFLAGS} pdp9 : ${BIN}pdp9${EXE} ${BIN}pdp9${EXE} : ${PDP18B} ${SIM} + ${MKDIRBIN} ${CC} ${PDP18B} ${SIM} ${PDP9_OPT} -o $@ ${LDFLAGS} pdp15 : ${BIN}pdp15${EXE} ${BIN}pdp15${EXE} : ${PDP18B} ${SIM} + ${MKDIRBIN} ${CC} ${PDP18B} ${SIM} ${PDP15_OPT} -o $@ ${LDFLAGS} pdp10 : ${BIN}pdp10${EXE} ${BIN}pdp10${EXE} : ${PDP10} ${SIM} + ${MKDIRBIN} ${CC} ${PDP10} ${SIM} ${PDP10_OPT} -o $@ ${LDFLAGS} pdp11 : ${BIN}pdp11${EXE} ${BIN}pdp11${EXE} : ${PDP11} ${SIM} + ${MKDIRBIN} ${CC} ${PDP11} ${SIM} ${PDP11_OPT} -o $@ ${LDFLAGS} vax : ${BIN}vax${EXE} ${BIN}vax${EXE} : ${VAX} ${SIM} + ${MKDIRBIN} ${CC} ${VAX} ${SIM} ${VAX_OPT} -o $@ ${LDFLAGS} vax780 : ${BIN}vax780${EXE} ${BIN}vax780${EXE} : ${VAX780} ${SIM} + ${MKDIRBIN} ${CC} ${VAX780} ${SIM} ${VAX780_OPT} -o $@ ${LDFLAGS} nova : ${BIN}nova${EXE} ${BIN}nova${EXE} : ${NOVA} ${SIM} + ${MKDIRBIN} ${CC} ${NOVA} ${SIM} ${NOVA_OPT} -o $@ ${LDFLAGS} eclipse : ${BIN}eclipse${EXE} ${BIN}eclipse${EXE} : ${ECLIPSE} ${SIM} + ${MKDIRBIN} ${CC} ${ECLIPSE} ${SIM} ${ECLIPSE_OPT} -o $@ ${LDFLAGS} h316 : ${BIN}h316${EXE} ${BIN}h316${EXE} : ${H316} ${SIM} + ${MKDIRBIN} ${CC} ${H316} ${SIM} ${H316_OPT} -o $@ ${LDFLAGS} hp2100 : ${BIN}hp2100${EXE} ${BIN}hp2100${EXE} : ${HP2100} ${SIM} + ${MKDIRBIN} ${CC} ${HP2100} ${SIM} ${HP2100_OPT} -o $@ ${LDFLAGS} i1401 : ${BIN}i1401${EXE} ${BIN}i1401${EXE} : ${I1401} ${SIM} + ${MKDIRBIN} ${CC} ${I1401} ${SIM} ${I1401_OPT} -o $@ ${LDFLAGS} i1620 : ${BIN}i1620${EXE} ${BIN}i1620${EXE} : ${I1620} ${SIM} + ${MKDIRBIN} ${CC} ${I1620} ${SIM} ${I1620_OPT} -o $@ ${LDFLAGS} i7094 : ${BIN}i7094${EXE} ${BIN}i7094${EXE} : ${I7094} ${SIM} + ${MKDIRBIN} ${CC} ${I7094} ${SIM} ${I7094_OPT} -o $@ ${LDFLAGS} ibm1130 : ${BIN}ibm1130${EXE} ${BIN}ibm1130${EXE} : ${IBM1130} + ${MKDIRBIN} ${CC} ${IBM1130} ${SIM} ${IBM1130_OPT} -o $@ ${LDFLAGS} s3 : ${BIN}s3${EXE} ${BIN}s3${EXE} : ${S3} ${SIM} + ${MKDIRBIN} ${CC} ${S3} ${SIM} ${S3_OPT} -o $@ ${LDFLAGS} altair : ${BIN}altair${EXE} ${BIN}altair${EXE} : ${ALTAIR} ${SIM} + ${MKDIRBIN} ${CC} ${ALTAIR} ${SIM} ${ALTAIR_OPT} -o $@ ${LDFLAGS} altairz80 : ${BIN}altairz80${EXE} ${BIN}altairz80${EXE} : ${ALTAIRZ80} ${SIM} + ${MKDIRBIN} ${CC} ${ALTAIRZ80} ${SIM} ${ALTAIRZ80_OPT} -o $@ ${LDFLAGS} gri : ${BIN}gri${EXE} ${BIN}gri${EXE} : ${GRI} ${SIM} + ${MKDIRBIN} ${CC} ${GRI} ${SIM} ${GRI_OPT} -o $@ ${LDFLAGS} lgp : ${BIN}lgp${EXE} ${BIN}lgp${EXE} : ${LGP} ${SIM} + ${MKDIRBIN} ${CC} ${LGP} ${SIM} ${LGP_OPT} -o $@ ${LDFLAGS} id16 : ${BIN}id16${EXE} ${BIN}id16${EXE} : ${ID16} ${SIM} + ${MKDIRBIN} ${CC} ${ID16} ${SIM} ${ID16_OPT} -o $@ ${LDFLAGS} id32 : ${BIN}id32${EXE} ${BIN}id32${EXE} : ${ID32} ${SIM} + ${MKDIRBIN} ${CC} ${ID32} ${SIM} ${ID32_OPT} -o $@ ${LDFLAGS} sds : ${BIN}sds${EXE} ${BIN}sds${EXE} : ${SDS} ${SIM} + ${MKDIRBIN} ${CC} ${SDS} ${SIM} ${SDS_OPT} -o $@ ${LDFLAGS} swtp : ${BIN}swtp${EXE} ${BIN}swtp${EXE} : ${SWTP} ${SIM} - ${CC} ${SWTP} ${SIM} ${SWTP_OPT} -o $@ ${LDFLAGS} \ No newline at end of file + ${MKDIRBIN} + ${CC} ${SWTP} ${SIM} ${SWTP_OPT} -o $@ ${LDFLAGS}