Merge branch 'master' into Extra-VAXen

Conflicts:
	makefile
This commit is contained in:
Mark Pizzolato 2011-11-10 09:04:42 -08:00
commit 3e10dfbd95
25 changed files with 2029 additions and 1269 deletions

View file

@ -5,6 +5,9 @@ Theory of operation.
Features. Features.
- Optional Use. Build with or without SIM_ASYNCH_IO defined and - Optional Use. Build with or without SIM_ASYNCH_IO defined and
simulators will still build and perform correctly when run. simulators will still build and perform correctly when run.
Additionmally, a simulator built with SIM_ASYNCH_IO defined can
dynamically disable and reenable asynchronous operation with
the scp commands SET NOASYNCH and SET ASYNCH respectively.
- Consistent Save/Restore state. The state of a simulator saved - Consistent Save/Restore state. The state of a simulator saved
on a simulator with (or without) Asynch support can be restored on a simulator with (or without) Asynch support can be restored
on any simulator of the same version with or without Asynch on any simulator of the same version with or without Asynch

View file

@ -43,9 +43,9 @@ bridge, or route TAP devices for you.
Integrated Universal TUN/TAP support can be used for host<->simulator network 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: 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 "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 tested on include: Linux, FreeBSD, OpenBSD, NetBSD and OSX. Each of these
has some way to create a tap pseudo device and then bridge it with a physical platforms has some way to create a tap pseudo device (and possibly then to
network interface. bridge it with a physical network interface).
The following steps were performed to get a working SIMH vax simulator The following steps were performed to get a working SIMH vax simulator
sharing a physical NIC and allowing Host<->SIMH vax communications: sharing a physical NIC and allowing Host<->SIMH vax communications:
@ -117,6 +117,53 @@ NetBSD (NetBSD 5.0.2)
# Run simulator and "attach xq tap:tap0" # Run simulator and "attach xq tap:tap0"
OSX (Snow Leopard)
OSX Does NOT have native support for tun/tap interfaces. It also does not have native
support for bridging.
Mattias Nissler has created tun/tap functionality available at http://tuntaposx,sourceforge.net/
We'll punt on bridging for the sake of this example and move on to use a basic tap
based internal network so a host and guest can communicate directly.
Download the install package from:
http://sourceforge.net/projects/tuntaposx/files/tuntap/20090913/tuntap_20090913.tar.gz
Expand the tarball to a directory.
Invoke the package installer tuntap_20090913.pkg
Click through the various prompts accepting things and eventually installing the package.
# Build and Run simulator and:
sim> attach xq tap:tap0
sim> ! ifconfig tap0 192.168.6.1 netmask 255.255.255.0
Simulated system uses IP address 192.168.6.2 and host uses 192.167.6.1
and things work.
You must run as root for this to work.
-------------------------------------------------------------------------------
An alternative to direct pcap and tun/tap networking on *nix environments is
VDE (Virtual Distributed Ethernet).
Note 1: Using vde based networking is likely more flexible, but it isn't
nearly as efficient. Host OS overhead will always be higher when
vde networking is used as compared to native pcap and/or tun/tap
networking.
Note 2: Root access will likely be needed to configure or start the vde
environment prior to starting a simulator which may use it.
Note 3: Simulators running using VDE networking can run without root
privilege.
Linux (Ubuntu 10.04):
apt-get install libvdeplug-dev
apt-get install vde2
vde_switch -s /tmp/switch1 -tap tap0 -m 666
ifconfig tap0 192.168.6.1 netmask 255.255.255.0 up
# Build and Run simulator and:
sim> attach xq vde:/tmp/switch1 #simulator uses IP address 192.168.6.2
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Windows notes: Windows notes:
@ -134,6 +181,11 @@ Windows notes:
Building on Windows: Building on Windows:
Building with MinGW can use the provided makefile following the instructions
below. Alternatively, you can use the free Visual C++ Express 2008 or 2010
interactive development environments. Read the file
".\Visual Studio Projects\0ReadMe_Projects.txt" for details.
1. Install WinPCAP 4.x 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,pcap,pcap-stdinc 2. Put the required .h files (bittypes,devioctl,ip6_misc,pcap,pcap-stdinc
@ -159,8 +211,11 @@ Linux, {Free|Net|Open}BSD, OS/X, and Un*x notes:
----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- WARNING -----
Sim_Ether has been reworked to be more universal; because of this, you will 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 need to get a version of libpcap that is 0.9 or greater. All current Linux
downloaded from www.tcpdump.org - see the comments at the top of Sim_ether.c distributions provide a libpcap-dev package which has the needed version
of libpcap and the required components to build applications using it.
If you are running an older Linux OS, you can download and build the required
library from www.tcpdump.org - see the comments at the top of Sim_ether.c
for details. for details.
----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- WARNING -----
@ -187,11 +242,13 @@ for details.
will be welcomed. will be welcomed.
2. If you want to use TAP devices, and any surrounding system network/bridge 2. If you want to use TAP devices, and any surrounding system network/bridge
setup must be done before running SIMH. setup must be done before running SIMH. However, once that is done
(possibly at system boot time), using the TAP devices can be done without
root privileges.
Building on Linux, {Free|Net|Open}BSD, OS/X, Un*x: Building on Linux, {Free|Net|Open}BSD, OS/X, Un*x:
1. Get/make/install the libpcap package for your operating system. Sources: 1. Get/make/install the libpcap-dev package for your operating system. Sources:
All : http://www.tcpdump.org/ All : http://www.tcpdump.org/
Older versions of libpcap can be found, for various systems, at: Older versions of libpcap can be found, for various systems, at:
Linux : search for your variant on http://rpmfind.net Linux : search for your variant on http://rpmfind.net
@ -214,7 +271,7 @@ Building on Linux, {Free|Net|Open}BSD, OS/X, Un*x:
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
OpenVMS Alpha notes: OpenVMS Alpha and OpenVMS Integrety (IA64) notes:
1. Ethernet support will only work on Alpha VMS 7.3-1 or later, which is 1. Ethernet support will only work on Alpha VMS 7.3-1 or later, which is
when required VCI promiscuous mode support was added. Hobbyists can when required VCI promiscuous mode support was added. Hobbyists can
get the required version of VMS from the OpenVMS Alpha Hobbyist Kit 3.0. get the required version of VMS from the OpenVMS Alpha Hobbyist Kit 3.0.
@ -247,10 +304,10 @@ OpenVMS Alpha notes:
adapter prior trying to connect with SIMH, or the host may crash. adapter prior trying to connect with SIMH, or the host may crash.
The execlet is not written to create an I/O structure for the device. The execlet is not written to create an I/O structure for the device.
Building on OpenVMS Alpha: Building on OpenVMS Alpha and OpenVMS Integrety (IA64):
The current descrip.mms file will build simulators capable of using 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 VAX780, and PDP11. The descrip.mms driven builds will also build the
pcap library and build and install the VCI execlet. pcap library and build and install the VCI execlet.
1. Fetch the VMS-PCAP zip file from: 1. Fetch the VMS-PCAP zip file from:
@ -321,6 +378,17 @@ Dave
Change Log Change Log
=============================================================================== ===============================================================================
30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking
29-Oct-11 MP Added support for integrated Tap networking interfaces on OSX
17-Aug-11 RMS Fix from Sergey Oboguev relating to XU and XQ Auto Config and
vector assignments
12-Aug-11 MP Cleaned up payload length determination
Fixed race condition detecting reflections when threaded
reading and writing is enabled
07-Jul-11 MB VMS Pcap (from Mike Burke)
- Fixed Alpha issues
- Added OpenVMS Integrety support
20-Apr-11 MP Fixed save/restore behavior
12-Jan-11 DTH Added SHOW XU FILTERS modifier 12-Jan-11 DTH Added SHOW XU FILTERS modifier
11-Jan-11 DTH Corrected DEUNA/DELUA SELFTEST command, enabling use by 11-Jan-11 DTH Corrected DEUNA/DELUA SELFTEST command, enabling use by
VMS 3.7, VMS 4.7, and Ultrix 1.1 VMS 3.7, VMS 4.7, and Ultrix 1.1

View file

@ -469,7 +469,7 @@ den = GET_DEN (tutc); /* get density */
uptr = tu_dev.units + drv; /* get unit */ uptr = tu_dev.units + drv; /* get unit */
if (DEBUG_PRS (tu_dev)) { if (DEBUG_PRS (tu_dev)) {
fprintf (sim_deb, ">>TU%d STRT: fnc=%s, fc=%06o, fs=%06o, er=%06o, pos=", fprintf (sim_deb, ">>TU%d STRT: fnc=%s, fc=%06o, fs=%06o, er=%06o, pos=",
drv, tu_fname[fnc], tufc, tufs, tuer, uptr->pos); drv, tu_fname[fnc], tufc, tufs, tuer);
fprint_val (sim_deb, uptr->pos, 10, T_ADDR_W, PV_LEFT); fprint_val (sim_deb, uptr->pos, 10, T_ADDR_W, PV_LEFT);
fprintf (sim_deb, "\n"); fprintf (sim_deb, "\n");
} }

View file

@ -2584,6 +2584,7 @@ t_stat xq_attach(UNIT* uptr, char* cptr)
if (xq->var->poll == 0) { if (xq->var->poll == 0) {
status = eth_set_async(xq->var->etherface, xq->var->coalesce_latency_ticks); status = eth_set_async(xq->var->etherface, xq->var->coalesce_latency_ticks);
if (status != SCPE_OK) { if (status != SCPE_OK) {
eth_close(xq->var->etherface);
free(tptr); free(tptr);
free(xq->var->etherface); free(xq->var->etherface);
xq->var->etherface = NULL; xq->var->etherface = NULL;

View file

@ -87,7 +87,7 @@ extern int32 int_req[IPL_HLVL];
#define XQ_QUE_MAX 500 /* read queue size in packets */ #define XQ_QUE_MAX 500 /* read queue size in packets */
#define XQ_FILTER_MAX 14 /* number of filters allowed */ #define XQ_FILTER_MAX 14 /* number of filters allowed */
#if defined SIM_ASYNCH_IO #if defined(SIM_ASYNCH_IO) && defined(USE_READER_THREAD)
#define XQ_SERVICE_INTERVAL 0 /* polling interval - No Polling with Asynch I/O */ #define XQ_SERVICE_INTERVAL 0 /* polling interval - No Polling with Asynch I/O */
#else #else
#define XQ_SERVICE_INTERVAL 100 /* polling interval - X per second */ #define XQ_SERVICE_INTERVAL 100 /* polling interval - X per second */

View file

@ -1,6 +1,6 @@
/* pdp8_fpp.c: PDP-8 floating point processor (FPP8A) /* pdp8_fpp.c: PDP-8 floating point processor (FPP8A)
Copyright (c) 2007-2011, Robert M Supnik Copyright (c) 2007-2010, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),

View file

@ -1,6 +1,6 @@
/* pdp8_sys.c: PDP-8 simulator interface /* pdp8_sys.c: PDP-8 simulator interface
Copyright (c) 1993-2011, Robert M Supnik Copyright (c) 1993-2009, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
@ -24,7 +24,7 @@
in this Software without prior written authorization from Robert M Supnik. in this Software without prior written authorization from Robert M Supnik.
24-Mar-09 RMS Added link to FPP 24-Mar-09 RMS Added link to FPP
24-Jun-08 RMS Fixed bug in new rim loader (Don North) 24-Jun-08 RMS Fixed bug in new rim loader (found by Don North)
24-May-08 RMS Fixed signed/unsigned declaration inconsistency 24-May-08 RMS Fixed signed/unsigned declaration inconsistency
03-Sep-07 RMS Added FPP8 support 03-Sep-07 RMS Added FPP8 support
Rewrote rim and binary loaders Rewrote rim and binary loaders

View file

@ -28,6 +28,7 @@
td TD8E/TU56 DECtape td TD8E/TU56 DECtape
23-Mar-11 RMS Fixed SDLC to clear AC (from Dave Gesswein)
23-Jun-06 RMS Fixed switch conflict in ATTACH 23-Jun-06 RMS Fixed switch conflict in ATTACH
16-Aug-05 RMS Fixed C++ declaration and cast problems 16-Aug-05 RMS Fixed C++ declaration and cast problems
09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR
@ -302,6 +303,7 @@ switch (pulse) {
if (td_newsa (td_cmd)) /* new command */ if (td_newsa (td_cmd)) /* new command */
return AC | (IORETURN (td_stopoffr, STOP_DTOFF) << IOT_V_REASON); return AC | (IORETURN (td_stopoffr, STOP_DTOFF) << IOT_V_REASON);
} }
AC = 0;
break; break;
case 05: /* SDLD */ case 05: /* SDLD */

View file

@ -30,11 +30,34 @@
todr TODR clock todr TODR clock
tmr interval timer tmr interval timer
28-Sep-11 MP Generalized setting TODR for all OSes.
Unbound the TODR value from the 100hz clock tick
interrupt. TODR now behaves like the original
battery backed-up clock and runs with the wall
clock, not the simulated instruction clock.
Two operational modes are available:
- Default VMS mode, which is similar to the previous
behavior in that without initializing the TODR it
would default to the value VMS would set it to if
VMS knew the correct time. This would be correct
almost all the time unless a VMS disk hadn't been
booted from for more than a year. This mode
produces strange time results for non VMS OSes on
each system boot.
- OS Agnostic mode. This mode behaves precisely like
the VAX780 TODR and works correctly for all OSes.
This mode is enabled by attaching the TODR to a
battery backup state file for the TOY clock
(i.e. sim> attach TODR TOY_CLOCK). When operating
in OS Agnostic mode, the TODR will initially start
counting from 0 and be adjusted differently when an
OS specifically writes to the TODR. VMS will prompt
to set the time on each boot unless the SYSGEN
parameter TIMEPROMPTWAIT is set to 0.
29-Mar-2011 MB First Version 29-Mar-2011 MB First Version
*/ */
#include "vax_defs.h" #include "vax_defs.h"
#include <time.h>
/* Terminal definitions */ /* Terminal definitions */
@ -159,6 +182,11 @@ int32 clk_tps = 100; /* ticks/second */
int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */ int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */
int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ int32 tmr_poll = CLK_DELAY; /* pgm timer poll */
int32 todr_reg = 0; /* TODR register */ int32 todr_reg = 0; /* TODR register */
struct todr_battery_info {
uint32 toy_gmtbase; /* GMT base of set value */
uint32 toy_gmtbasemsec; /* The milliseconds of the set value */
};
typedef struct todr_battery_info TOY;
int32 td_swait = 100; /* seek, per block */ int32 td_swait = 100; /* seek, per block */
int32 td_cwait = 150; /* command time */ int32 td_cwait = 150; /* command time */
@ -187,6 +215,8 @@ t_stat tmr_svc (UNIT *uptr);
t_stat tti_reset (DEVICE *dptr); t_stat tti_reset (DEVICE *dptr);
t_stat tto_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr);
t_stat clk_reset (DEVICE *dptr); t_stat clk_reset (DEVICE *dptr);
t_stat clk_attach (UNIT *uptr, char *cptr);
t_stat clk_detach (UNIT *uptr);
t_stat tmr_reset (DEVICE *dptr); t_stat tmr_reset (DEVICE *dptr);
t_stat td_svc (UNIT *uptr); t_stat td_svc (UNIT *uptr);
t_stat td_reset (DEVICE *dptr); t_stat td_reset (DEVICE *dptr);
@ -269,7 +299,7 @@ DEVICE tto_dev = {
/* TODR and TMR data structures */ /* TODR and TMR data structures */
UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), CLK_DELAY }; /* 100Hz */ UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE+UNIT_FIX, sizeof(TOY)), CLK_DELAY };/* 100Hz */
REG clk_reg[] = { REG clk_reg[] = {
{ DRDATA (TODR, todr_reg, 32), PV_LEFT }, { DRDATA (TODR, todr_reg, 32), PV_LEFT },
@ -280,9 +310,9 @@ REG clk_reg[] = {
DEVICE clk_dev = { DEVICE clk_dev = {
"TODR", &clk_unit, clk_reg, NULL, "TODR", &clk_unit, clk_reg, NULL,
1, 0, 0, 0, 0, 0, 1, 0, 8, 1, 0, 0,
NULL, NULL, &clk_reset, NULL, NULL, &clk_reset,
NULL, NULL, NULL, NULL, &clk_attach, &clk_detach,
NULL, 0 NULL, 0
}; };
@ -763,7 +793,6 @@ t_stat clk_svc (UNIT *uptr)
tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */
sim_activate (&clk_unit, tmr_poll); /* reactivate unit */ sim_activate (&clk_unit, tmr_poll); /* reactivate unit */
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */ tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
todr_reg = todr_reg + 1; /* incr TODR */
if ((tmr_iccs & TMR_CSR_RUN) && tmr_use_100hz) /* timer on, std intvl? */ if ((tmr_iccs & TMR_CSR_RUN) && tmr_use_100hz) /* timer on, std intvl? */
tmr_incr (TMR_INC); /* do timer service */ tmr_incr (TMR_INC); /* do timer service */
return SCPE_OK; return SCPE_OK;
@ -836,9 +865,44 @@ t_stat clk_reset (DEVICE *dptr)
tmr_poll = sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init 100Hz timer */ tmr_poll = sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init 100Hz timer */
sim_activate_abs (&clk_unit, tmr_poll); /* activate 100Hz unit */ sim_activate_abs (&clk_unit, tmr_poll); /* activate 100Hz unit */
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */ tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
if (clk_unit.filebuf == NULL) { /* make sure the TODR is initialized */
clk_unit.filebuf = calloc(sizeof(TOY), 1);
if (clk_unit.filebuf == NULL)
return SCPE_MEM;
todr_resync ();
}
return SCPE_OK; return SCPE_OK;
} }
/* CLK attach */
t_stat clk_attach (UNIT *uptr, char *cptr)
{
t_stat r;
uptr->flags = uptr->flags | (UNIT_ATTABLE | UNIT_BUFABLE);
memset (uptr->filebuf, 0, (size_t)uptr->capac);
r = attach_unit (uptr, cptr);
if (r != SCPE_OK)
uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE);
else
uptr->hwmark = (uint32) uptr->capac;
return r;
}
/* CLK detach */
t_stat clk_detach (UNIT *uptr)
{
t_stat r;
r = detach_unit (uptr);
if ((uptr->flags & UNIT_ATT) == 0)
uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE);
return r;
}
/* Interval timer reset */ /* Interval timer reset */
t_stat tmr_reset (DEVICE *dptr) t_stat tmr_reset (DEVICE *dptr)
@ -857,18 +921,43 @@ return SCPE_OK;
int32 todr_rd (void) int32 todr_rd (void)
{ {
return todr_reg; TOY *toy = (TOY *)clk_unit.filebuf;
struct timespec base, now, val;
clock_gettime(CLOCK_REALTIME, &now); /* get curr time */
base.tv_sec = toy->toy_gmtbase;
base.tv_nsec = toy->toy_gmtbasemsec * 1000000;
sim_timespec_diff (&val, &now, &base);
return (int32)(val.tv_sec*100 + val.tv_nsec/10000000); /* 100hz Clock Ticks */
} }
void todr_wr (int32 data) void todr_wr (int32 data)
{ {
todr_reg = data; TOY *toy = (TOY *)clk_unit.filebuf;
return; struct timespec now, val, base;
/* Save the GMT time when set value was 0 to record the base for future
read operations in "battery backed-up" state */
if (-1 == clock_gettime(CLOCK_REALTIME, &now)) /* get curr time */
return; /* error? */
val.tv_sec = ((uint32)data) / 100;
val.tv_nsec = (((uint32)data) % 100) * 10000000;
sim_timespec_diff (&base, &now, &val); /* base = now - data */
toy->toy_gmtbase = base.tv_sec;
toy->toy_gmtbasemsec = base.tv_nsec/1000000;
} }
t_stat todr_resync (void) t_stat todr_resync (void)
{ {
uint32 base; TOY *toy = (TOY *)clk_unit.filebuf;
if (clk_unit.flags & UNIT_ATT) { /* Attached means behave like real VAX780 */
if (!toy->toy_gmtbase) /* Never set? */
todr_wr (0); /* Start ticking from 0 */
}
else { /* Not-Attached means */
uint32 base; /* behave like simh VMS default */
time_t curr; time_t curr;
struct tm *ctm; struct tm *ctm;
@ -882,7 +971,8 @@ base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */
ctm->tm_hour) * 60) + ctm->tm_hour) * 60) +
ctm->tm_min) * 60) + ctm->tm_min) * 60) +
ctm->tm_sec; ctm->tm_sec;
todr_reg = (base * 100) + 0x10000000; /* cvt to VAX form */ todr_wr ((base * 100) + 0x10000000); /* use VMS form */
}
return SCPE_OK; return SCPE_OK;
} }

View file

@ -29,6 +29,30 @@
todr TODR clock todr TODR clock
tmr interval timer tmr interval timer
28-Sep-11 MP Generalized setting TODR for all OSes.
Unbound the TODR value from the 100hz clock tick
interrupt. TODR now behaves like the original
battery backed-up clock and runs with the wall
clock, not the simulated instruction clock.
Two operational modes are available:
- Default VMS mode, which is similar to the previous
behavior in that without initializing the TODR it
would default to the value VMS would set it to if
VMS knew the correct time. This would be correct
almost all the time unless a VMS disk hadn't been
booted from for more than a year. This mode
produces strange time results for non VMS OSes on
each system boot.
- OS Agnostic mode. This mode behaves precisely like
the VAX780 TODR and works correctly for all OSes.
This mode is enabled by attaching the TODR to a
battery backup state file for the TOY clock
(i.e. sim> attach TODR TOY_CLOCK). When operating
in OS Agnostic mode, the TODR will initially start
counting from 0 and be adjusted differently when an
OS specifically writes to the TODR. VMS will prompt
to set the time on each boot unless the SYSGEN
parameter TIMEPROMPTWAIT is set to 0.
21-Mar-11 RMS Added reboot capability 21-Mar-11 RMS Added reboot capability
17-Aug-08 RMS Resync TODR on any clock reset 17-Aug-08 RMS Resync TODR on any clock reset
18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock 18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock
@ -56,7 +80,7 @@
*/ */
#include "vax_defs.h" #include "vax_defs.h"
#include <time.h>
/* Terminal definitions */ /* Terminal definitions */
@ -171,6 +195,11 @@ int32 clk_tps = 100; /* ticks/second */
int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */ int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */
int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ int32 tmr_poll = CLK_DELAY; /* pgm timer poll */
int32 todr_reg = 0; /* TODR register */ int32 todr_reg = 0; /* TODR register */
struct todr_battery_info {
uint32 toy_gmtbase; /* GMT base of set value */
uint32 toy_gmtbasemsec; /* The milliseconds of the set value */
};
typedef struct todr_battery_info TOY;
int32 fl_fnc = 0; /* function */ int32 fl_fnc = 0; /* function */
int32 fl_esr = 0; /* error status */ int32 fl_esr = 0; /* error status */
@ -197,6 +226,8 @@ t_stat tmr_svc (UNIT *uptr);
t_stat tti_reset (DEVICE *dptr); t_stat tti_reset (DEVICE *dptr);
t_stat tto_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr);
t_stat clk_reset (DEVICE *dptr); t_stat clk_reset (DEVICE *dptr);
t_stat clk_attach (UNIT *uptr, char *cptr);
t_stat clk_detach (UNIT *uptr);
t_stat tmr_reset (DEVICE *dptr); t_stat tmr_reset (DEVICE *dptr);
t_stat fl_svc (UNIT *uptr); t_stat fl_svc (UNIT *uptr);
t_stat fl_reset (DEVICE *dptr); t_stat fl_reset (DEVICE *dptr);
@ -281,7 +312,7 @@ DEVICE tto_dev = {
/* TODR and TMR data structures */ /* TODR and TMR data structures */
UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), CLK_DELAY }; /* 100Hz */ UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE+UNIT_FIX, sizeof(TOY)), CLK_DELAY };/* 100Hz */
REG clk_reg[] = { REG clk_reg[] = {
{ DRDATA (TODR, todr_reg, 32), PV_LEFT }, { DRDATA (TODR, todr_reg, 32), PV_LEFT },
@ -296,9 +327,9 @@ REG clk_reg[] = {
DEVICE clk_dev = { DEVICE clk_dev = {
"TODR", &clk_unit, clk_reg, NULL, "TODR", &clk_unit, clk_reg, NULL,
1, 0, 0, 0, 0, 0, 1, 0, 8, 1, 0, 0,
NULL, NULL, &clk_reset, NULL, NULL, &clk_reset,
NULL, NULL, NULL, NULL, &clk_attach, &clk_detach,
NULL, 0 NULL, 0
}; };
@ -578,7 +609,6 @@ tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */
sim_activate (&clk_unit, tmr_poll); /* reactivate unit */ sim_activate (&clk_unit, tmr_poll); /* reactivate unit */
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */ tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
AIO_SET_INTERRUPT_LATENCY(tmr_poll*clk_tps); /* set interrrupt latency */ 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? */ if ((tmr_iccs & TMR_CSR_RUN) && tmr_use_100hz) /* timer on, std intvl? */
tmr_incr (TMR_INC); /* do timer service */ tmr_incr (TMR_INC); /* do timer service */
return SCPE_OK; return SCPE_OK;
@ -651,9 +681,44 @@ t_stat clk_reset (DEVICE *dptr)
tmr_poll = sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init 100Hz timer */ tmr_poll = sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init 100Hz timer */
sim_activate_abs (&clk_unit, tmr_poll); /* activate 100Hz unit */ sim_activate_abs (&clk_unit, tmr_poll); /* activate 100Hz unit */
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */ tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
if (clk_unit.filebuf == NULL) { /* make sure the TODR is initialized */
clk_unit.filebuf = calloc(sizeof(TOY), 1);
if (clk_unit.filebuf == NULL)
return SCPE_MEM;
todr_resync ();
}
return SCPE_OK; return SCPE_OK;
} }
/* CLK attach */
t_stat clk_attach (UNIT *uptr, char *cptr)
{
t_stat r;
uptr->flags = uptr->flags | (UNIT_ATTABLE | UNIT_BUFABLE);
memset (uptr->filebuf, 0, (size_t)uptr->capac);
r = attach_unit (uptr, cptr);
if (r != SCPE_OK)
uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE);
else
uptr->hwmark = (uint32) uptr->capac;
return r;
}
/* CLK detach */
t_stat clk_detach (UNIT *uptr)
{
t_stat r;
r = detach_unit (uptr);
if ((uptr->flags & UNIT_ATT) == 0)
uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE);
return r;
}
/* Interval timer reset */ /* Interval timer reset */
t_stat tmr_reset (DEVICE *dptr) t_stat tmr_reset (DEVICE *dptr)
@ -672,18 +737,44 @@ return SCPE_OK;
int32 todr_rd (void) int32 todr_rd (void)
{ {
return todr_reg; TOY *toy = (TOY *)clk_unit.filebuf;
struct timespec base, now, val;
clock_gettime(CLOCK_REALTIME, &now); /* get curr time */
base.tv_sec = toy->toy_gmtbase;
base.tv_nsec = toy->toy_gmtbasemsec * 1000000;
sim_timespec_diff (&val, &now, &base);
return (int32)(val.tv_sec*100 + val.tv_nsec/10000000); /* 100hz Clock Ticks */
} }
void todr_wr (int32 data) void todr_wr (int32 data)
{ {
todr_reg = data; TOY *toy = (TOY *)clk_unit.filebuf;
return; struct timespec now, val, base;
/* Save the GMT time when set value was 0 to record the base for future
read operations in "battery backed-up" state */
if (-1 == clock_gettime(CLOCK_REALTIME, &now)) /* get curr time */
return; /* error? */
val.tv_sec = ((uint32)data) / 100;
val.tv_nsec = (((uint32)data) % 100) * 10000000;
sim_timespec_diff (&base, &now, &val); /* base = now - data */
toy->toy_gmtbase = base.tv_sec;
toy->toy_gmtbasemsec = base.tv_nsec/1000000;
} }
t_stat todr_resync (void) t_stat todr_resync (void)
{ {
uint32 base; TOY *toy = (TOY *)clk_unit.filebuf;
if (clk_unit.flags & UNIT_ATT) { /* Attached means behave like real VAX780 */
if (!toy->toy_gmtbase) /* Never set? */
todr_wr (0); /* Start ticking from 0 */
}
else { /* Not-Attached means */
uint32 base; /* behave like simh VMS default */
time_t curr; time_t curr;
struct tm *ctm; struct tm *ctm;
@ -697,7 +788,8 @@ base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */
ctm->tm_hour) * 60) + ctm->tm_hour) * 60) +
ctm->tm_min) * 60) + ctm->tm_min) * 60) +
ctm->tm_sec; ctm->tm_sec;
todr_reg = (base * 100) + 0x10000000; /* cvt to VAX form */ todr_wr ((base * 100) + 0x10000000); /* use VMS form */
}
return SCPE_OK; return SCPE_OK;
} }

View file

@ -27,6 +27,31 @@
tto terminal output tto terminal output
clk 100Hz and TODR clock clk 100Hz and TODR clock
28-Sep-11 MP Generalized setting TODR for all OSes.
Unbound the TODR value from the 100hz clock tick
interrupt. TODR now behaves like the original
battery backed-up clock and runs with the wall
clock, not the simulated instruction clock
(except when running ROM diagnostics).
Two operational modes are available:
- Default VMS mode, which is similar to the previous
behavior in that without initializing the TODR it
would default to the value VMS would set it to if
VMS knew the correct time. This would be correct
almost all the time unless a VMS disk hadn't been
booted from for more than a year. This mode
produces strange time results for non VMS OSes on
each system boot.
- OS Agnostic mode. This mode behaves precisely like
the VAX780 TODR and works correctly for all OSes.
This mode is enabled by attaching the TODR to a
battery backup state file for the TOY clock
(i.e. sim> attach TODR TOY_CLOCK). When operating
in OS Agnostic mode, the TODR will initially start
counting from 0 and be adjusted differently when an
OS specifically writes to the TODR. VMS will prompt
to set the time on each boot unless the SYSGEN
parameter TIMEPROMPTWAIT is set to 0.
05-Jan-11 MP Added Asynch I/O support 05-Jan-11 MP Added Asynch I/O support
17-Aug-08 RMS Resync TODR on any clock reset 17-Aug-08 RMS Resync TODR on any clock reset
18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock 18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock
@ -70,6 +95,11 @@ int32 clk_csr = 0; /* control/status */
int32 clk_tps = 100; /* ticks/second */ int32 clk_tps = 100; /* ticks/second */
int32 todr_reg = 0; /* TODR register */ int32 todr_reg = 0; /* TODR register */
int32 todr_blow = 1; /* TODR battery low */ int32 todr_blow = 1; /* TODR battery low */
struct todr_battery_info {
uint32 toy_gmtbase; /* GMT base of set value */
uint32 toy_gmtbasemsec; /* The milliseconds of the set value */
};
typedef struct todr_battery_info TOY;
int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */ int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */
int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ int32 tmr_poll = CLK_DELAY; /* pgm timer poll */
@ -79,9 +109,12 @@ t_stat clk_svc (UNIT *uptr);
t_stat tti_reset (DEVICE *dptr); t_stat tti_reset (DEVICE *dptr);
t_stat tto_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr);
t_stat clk_reset (DEVICE *dptr); t_stat clk_reset (DEVICE *dptr);
t_stat clk_attach (UNIT *uptr, char *cptr);
t_stat clk_detach (UNIT *uptr);
t_stat todr_resync (void); t_stat todr_resync (void);
extern int32 sysd_hlt_enb (void); extern int32 sysd_hlt_enb (void);
extern int32 fault_PC;
/* TTI data structures /* TTI data structures
@ -168,7 +201,7 @@ DEVICE tto_dev = {
DIB clk_dib = { 0, 0, NULL, NULL, 1, IVCL (CLK), SCB_INTTIM, { NULL } }; DIB clk_dib = { 0, 0, NULL, NULL, 1, IVCL (CLK), SCB_INTTIM, { NULL } };
UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), CLK_DELAY }; UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE+UNIT_FIX, sizeof(TOY)), CLK_DELAY };/* 100Hz */
REG clk_reg[] = { REG clk_reg[] = {
{ HRDATA (CSR, clk_csr, 16) }, { HRDATA (CSR, clk_csr, 16) },
@ -180,6 +213,7 @@ REG clk_reg[] = {
{ DRDATA (POLL, tmr_poll, 24), REG_NZ + PV_LEFT + REG_HRO }, { DRDATA (POLL, tmr_poll, 24), REG_NZ + PV_LEFT + REG_HRO },
{ DRDATA (TPS, clk_tps, 8), REG_NZ + PV_LEFT }, { DRDATA (TPS, clk_tps, 8), REG_NZ + PV_LEFT },
#if defined (SIM_ASYNCH_IO) #if defined (SIM_ASYNCH_IO)
{ DRDATA (ASYNCH, sim_asynch_enabled, 1), PV_LEFT },
{ DRDATA (LATENCY, sim_asynch_latency, 32), PV_LEFT }, { DRDATA (LATENCY, sim_asynch_latency, 32), PV_LEFT },
{ DRDATA (INST_LATENCY, sim_asynch_inst_latency, 32), PV_LEFT }, { DRDATA (INST_LATENCY, sim_asynch_inst_latency, 32), PV_LEFT },
#endif #endif
@ -193,16 +227,15 @@ MTAB clk_mod[] = {
DEVICE clk_dev = { DEVICE clk_dev = {
"CLK", &clk_unit, clk_reg, clk_mod, "CLK", &clk_unit, clk_reg, clk_mod,
1, 0, 0, 0, 0, 0, 1, 0, 8, 1, 0, 0,
NULL, NULL, &clk_reset, NULL, NULL, &clk_reset,
NULL, NULL, NULL, NULL, &clk_attach, &clk_detach,
&clk_dib, 0 &clk_dib, 0
}; };
/* Clock and terminal MxPR routines /* Clock and terminal MxPR routines
iccs_rd/wr interval timer iccs_rd/wr interval timer
todr_rd/wr time of year clock
rxcs_rd/wr input control/status rxcs_rd/wr input control/status
rxdb_rd input buffer rxdb_rd input buffer
txcs_rd/wr output control/status txcs_rd/wr output control/status
@ -214,11 +247,6 @@ int32 iccs_rd (void)
return (clk_csr & CLKCSR_IMP); return (clk_csr & CLKCSR_IMP);
} }
int32 todr_rd (void)
{
return todr_reg;
}
int32 rxcs_rd (void) int32 rxcs_rd (void)
{ {
return (tti_csr & TTICSR_IMP); return (tti_csr & TTICSR_IMP);
@ -247,14 +275,6 @@ clk_csr = (clk_csr & ~CLKCSR_RW) | (data & CLKCSR_RW);
return; return;
} }
void todr_wr (int32 data)
{
todr_reg = data;
if (data)
todr_blow = 0;
return;
}
void rxcs_wr (int32 data) void rxcs_wr (int32 data)
{ {
if ((data & CSR_IE) == 0) if ((data & CSR_IE) == 0)
@ -357,7 +377,8 @@ return SCPE_OK;
clk_svc process event (clock tick) clk_svc process event (clock tick)
clk_reset process reset clk_reset process reset
todr_powerup powerup for TODR (get date from system) todr_rd/wr time of year clock
todr_resync powerup for TODR (get date from system)
*/ */
t_stat clk_svc (UNIT *uptr) t_stat clk_svc (UNIT *uptr)
@ -370,8 +391,8 @@ t = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */
sim_activate (&clk_unit, t); /* reactivate unit */ sim_activate (&clk_unit, t); /* reactivate unit */
tmr_poll = t; /* set tmr poll */ tmr_poll = t; /* set tmr poll */
tmxr_poll = t * TMXR_MULT; /* set mux poll */ tmxr_poll = t * TMXR_MULT; /* set mux poll */
if (!todr_blow) /* incr TODR */ if (!todr_blow && todr_reg) /* if running? */
todr_reg = todr_reg + 1; todr_reg = todr_reg + 1; /* incr TODR */
return SCPE_OK; return SCPE_OK;
} }
@ -385,11 +406,65 @@ t = sim_is_active (&clk_unit);
return (t? t - 1: wait); return (t? t - 1: wait);
} }
int32 todr_rd (void)
{
TOY *toy = (TOY *)clk_unit.filebuf;
struct timespec base, now, val;
if ((fault_PC&0xFFFE0000) == 0x20040000) /* running from ROM? */
return todr_reg; /* return counted value for ROM diags */
if (0 == todr_reg) /* clock running? */
return todr_reg;
/* Maximum number of seconds which can be represented as 10ms ticks
in the 32bit TODR. This is the 33bit value 0x100000000/100 to get seconds */
#define TOY_MAX_SECS (0x40000000/25)
clock_gettime(CLOCK_REALTIME, &now); /* get curr time */
base.tv_sec = toy->toy_gmtbase;
base.tv_nsec = toy->toy_gmtbasemsec * 1000000;
sim_timespec_diff (&val, &now, &base);
if (val.tv_sec >= TOY_MAX_SECS) /* todr overflowed? */
return todr_reg = 0; /* stop counting */
return (int32)(val.tv_sec*100 + val.tv_nsec/10000000); /* 100hz Clock Ticks */
}
void todr_wr (int32 data)
{
TOY *toy = (TOY *)clk_unit.filebuf;
struct timespec now, val, base;
/* Save the GMT time when set value was 0 to record the base for future
read operations in "battery backed-up" state */
if (-1 == clock_gettime(CLOCK_REALTIME, &now)) /* get curr time */
return; /* error? */
val.tv_sec = ((uint32)data) / 100;
val.tv_nsec = (((uint32)data) % 100) * 10000000;
sim_timespec_diff (&base, &now, &val); /* base = now - data */
toy->toy_gmtbase = base.tv_sec;
toy->toy_gmtbasemsec = base.tv_nsec/1000000;
todr_reg = data;
if (data)
todr_blow = 0;
}
/* TODR resync routine */ /* TODR resync routine */
t_stat todr_resync (void) t_stat todr_resync (void)
{ {
uint32 base; TOY *toy = (TOY *)clk_unit.filebuf;
if (clk_unit.flags & UNIT_ATT) { /* Attached means behave like real VAX780 */
if (!toy->toy_gmtbase) /* Never set? */
todr_wr (0); /* Start ticking from 0 */
}
else { /* Not-Attached means */
uint32 base; /* behave like simh VMS default */
time_t curr; time_t curr;
struct tm *ctm; struct tm *ctm;
@ -403,8 +478,8 @@ base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */
ctm->tm_hour) * 60) + ctm->tm_hour) * 60) +
ctm->tm_min) * 60) + ctm->tm_min) * 60) +
ctm->tm_sec; ctm->tm_sec;
todr_reg = (base * 100) + 0x10000000; /* cvt to VAX form */ todr_wr ((base * 100) + 0x10000000); /* use VMS form */
todr_blow = 0; }
return SCPE_OK; return SCPE_OK;
} }
@ -414,13 +489,46 @@ t_stat clk_reset (DEVICE *dptr)
{ {
int32 t; int32 t;
todr_resync (); /* resync clock */
clk_csr = 0; clk_csr = 0;
CLR_INT (CLK); CLR_INT (CLK);
t = sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init timer */ t = sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init timer */
sim_activate_abs (&clk_unit, t); /* activate unit */ sim_activate_abs (&clk_unit, t); /* activate unit */
tmr_poll = t; /* set tmr poll */ tmr_poll = t; /* set tmr poll */
tmxr_poll = t * TMXR_MULT; /* set mux poll */ tmxr_poll = t * TMXR_MULT; /* set mux poll */
if (clk_unit.filebuf == NULL) { /* make sure the TODR is initialized */
clk_unit.filebuf = calloc(sizeof(TOY), 1);
if (clk_unit.filebuf == NULL)
return SCPE_MEM;
todr_resync ();
}
return SCPE_OK; return SCPE_OK;
} }
/* CLK attach */
t_stat clk_attach (UNIT *uptr, char *cptr)
{
t_stat r;
uptr->flags = uptr->flags | (UNIT_ATTABLE | UNIT_BUFABLE);
memset (uptr->filebuf, 0, (size_t)uptr->capac);
r = attach_unit (uptr, cptr);
if (r != SCPE_OK)
uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE);
else
uptr->hwmark = (uint32) uptr->capac;
return r;
}
/* CLK detach */
t_stat clk_detach (UNIT *uptr)
{
t_stat r;
r = detach_unit (uptr);
if ((uptr->flags & UNIT_ATT) == 0)
uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE);
return r;
}

View file

@ -134,7 +134,7 @@
FavorSizeOrSpeed="1" FavorSizeOrSpeed="1"
OmitFramePointers="true" OmitFramePointers="true"
AdditionalIncludeDirectories="./;../;../VAX/;../pdp11/;../../winpcap/Wpdpack/Include;&quot;../../pthreads/Pre-built.2/include&quot;" AdditionalIncludeDirectories="./;../;../VAX/;../pdp11/;../../winpcap/Wpdpack/Include;&quot;../../pthreads/Pre-built.2/include&quot;"
PreprocessorDefinitions="USE_INT64;USE_ADDR64;VM_VAX;VAX_730;USE_SHARED;_CRT_SECURE_NO_WARNINGS;USE_READER_THREAD" PreprocessorDefinitions="USE_INT64;USE_ADDR64;VM_VAX;VAX_730;USE_SHARED;_CRT_SECURE_NO_WARNINGS;USE_READER_THREAD;SIM_ASYNCH_IO"
StringPooling="true" StringPooling="true"
RuntimeLibrary="0" RuntimeLibrary="0"
EnableFunctionLevelLinking="true" EnableFunctionLevelLinking="true"

View file

@ -27,7 +27,7 @@
<Tool <Tool
Name="VCPreBuildEventTool" Name="VCPreBuildEventTool"
Description="Build Dependent ROM include File(s)" Description="Build Dependent ROM include File(s)"
CommandLine="pushd ..&#x0D;&#x0A;$(TargetDir)BuildROMs&#x0D;&#x0A;popd" CommandLine="pushd ..&#x0D;&#x0A;$(TargetDir)BuildROMs&#x0D;&#x0A;popd&#x0D;&#x0A;"
/> />
<Tool <Tool
Name="VCCustomBuildTool" Name="VCCustomBuildTool"
@ -112,7 +112,7 @@
<Tool <Tool
Name="VCPreBuildEventTool" Name="VCPreBuildEventTool"
Description="Build Dependent ROM include File(s)" Description="Build Dependent ROM include File(s)"
CommandLine="pushd ..&#x0D;&#x0A;$(TargetDir)BuildROMs&#x0D;&#x0A;popd" CommandLine="pushd ..&#x0D;&#x0A;$(TargetDir)BuildROMs&#x0D;&#x0A;popd&#x0D;&#x0A;"
/> />
<Tool <Tool
Name="VCCustomBuildTool" Name="VCCustomBuildTool"
@ -134,7 +134,7 @@
FavorSizeOrSpeed="1" FavorSizeOrSpeed="1"
OmitFramePointers="true" OmitFramePointers="true"
AdditionalIncludeDirectories="./;../;../VAX/;../pdp11/;../../winpcap/Wpdpack/Include;&quot;../../pthreads/Pre-built.2/include&quot;" AdditionalIncludeDirectories="./;../;../VAX/;../pdp11/;../../winpcap/Wpdpack/Include;&quot;../../pthreads/Pre-built.2/include&quot;"
PreprocessorDefinitions="USE_INT64;USE_ADDR64;VM_VAX;VAX_780;USE_SHARED;_CRT_SECURE_NO_WARNINGS;USE_READER_THREAD" PreprocessorDefinitions="USE_INT64;USE_ADDR64;VM_VAX;VAX_780;USE_SHARED;_CRT_SECURE_NO_WARNINGS;USE_READER_THREAD;SIM_ASYNCH_IO"
StringPooling="true" StringPooling="true"
RuntimeLibrary="0" RuntimeLibrary="0"
EnableFunctionLevelLinking="true" EnableFunctionLevelLinking="true"

View file

@ -57,8 +57,8 @@
# #
# MMK/MACRO=(NOASYNCH=1) # MMK/MACRO=(NOASYNCH=1)
# #
# On AXP the AXP PCAP components are built and used to provide network # On AXP and IA64 the VMS PCAP components are built and used to provide
# support for the VAX and PDP11 simulators. # network support for the VAX and PDP11 simulators.
# #
# The AXP PCAP components can only be built using a version of the # The AXP PCAP components can only be built using a version of the
# DEC/Compaq/HP Compiler version V6.5-001 or later. To build using an # DEC/Compaq/HP Compiler version V6.5-001 or later. To build using an

View file

@ -13,6 +13,7 @@
# #
ifeq ($(WIN32),) ifeq ($(WIN32),)
#*nix Environments (&& cygwin) #*nix Environments (&& cygwin)
GCC = gcc
ifeq (SunOS,$(shell uname)) ifeq (SunOS,$(shell uname))
TEST = /bin/test TEST = /bin/test
else else
@ -23,16 +24,20 @@ ifeq ($(WIN32),)
else else
ifeq (Linux,$(shell uname)) ifeq (Linux,$(shell uname))
LIBEXT = so LIBEXT = so
else
ifeq (SunOS,$(shell uname))
LIBEXT = so
else else
LIBEXT = a LIBEXT = a
endif endif
endif endif
endif
OS_CCDEFS = -D_GNU_SOURCE OS_CCDEFS = -D_GNU_SOURCE
ifeq (libm,$(shell if $(TEST) -e /usr/lib/libm.$(LIBEXT); then echo libm; fi)) ifeq (libm,$(shell if $(TEST) -e /usr/lib/libm.$(LIBEXT) -o -e /usr/lib64/libm.$(LIBEXT); then echo libm; fi))
OS_LDFLAGS += -lm OS_LDFLAGS += -lm
endif endif
ifeq (SunOS,$(shell uname)) ifeq (SunOS,$(shell uname))
OS_CCDEFS += -I/opt/sfw/include OS_CCDEFS += -I/opt/sfw/include -DSIM_ASYNCH_IO -DUSE_READER_THREAD
OS_LDFLAGS += -lsocket -lnsl -lrt -lm -lpthread -L/opt/sfw/lib -R/opt/sfw/lib OS_LDFLAGS += -lsocket -lnsl -lrt -lm -lpthread -L/opt/sfw/lib -R/opt/sfw/lib
endif endif
ifeq (cygwin,$(findstring cygwin,$(OSTYPE))) ifeq (cygwin,$(findstring cygwin,$(OSTYPE)))
@ -46,29 +51,41 @@ ifeq ($(WIN32),)
OS_LDFLAGS += -lpthread OS_LDFLAGS += -lpthread
endif endif
ifeq (readline,$(shell if $(TEST) -e /usr/lib/libreadline.$(LIBEXT) -o -e /usr/lib64/libreadline.$(LIBEXT) -o -e /opt/sfw/lib/libreadline.a; then echo readline; fi)) ifeq (readline,$(shell if $(TEST) -e /usr/lib/libreadline.$(LIBEXT) -o -e /usr/lib64/libreadline.$(LIBEXT) -o -e /opt/sfw/lib/libreadline.a; then echo readline; fi))
ifeq (readline_h,$(shell if $(TEST) -e /usr/include/readline/readline.h -o -e /usr/include/readline.h; then echo readline_h; fi)) ifeq (readline_h,$(shell if $(TEST) -e /usr/include/readline/readline.h -o -e /usr/include/readline.h -o -e /opt/sfw/include/readline/readline.h; then echo readline_h; fi))
# Use Locally installed and available readline support # 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)) 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_CCDEFS += -DHAVE_READLINE
OS_LDFLAGS += -lreadline -lncurses OS_LDFLAGS += -lreadline -lncurses
else else
ifeq (curses,$(shell if $(TEST) -e /usr/lib/libcurses.$(LIBEXT); then echo curses; fi))
OS_CCDEFS += -DHAVE_READLINE
OS_LDFLAGS += -lreadline -lcurses
else
ifeq (solaris_readline,$(shell if $(TEST) ! -e /opt/sfw/lib/libreadline.a; then echo solaris_readline; fi))
OS_CCDEFS += -DHAVE_READLINE OS_CCDEFS += -DHAVE_READLINE
OS_LDFLAGS += -lreadline OS_LDFLAGS += -lreadline
endif endif
endif endif
endif endif
endif
endif
ifeq (pcap,$(shell if $(TEST) -e /usr/include/pcap.h -o -e /opt/sfw/include/pcap.h; then echo pcap; fi)) 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 # Use Locally installed and available pcap support
NETWORK_CCDEFS = -DUSE_NETWORK NETWORK_CCDEFS = -DUSE_NETWORK
NETWORK_LDFLAGS = -lpcap NETWORK_LDFLAGS = -lpcap
endif endif
ifeq (vde,$(shell if $(TEST) -e /usr/include/libvdeplug.h -a \( -e /usr/lib/libvdeplug.$(LIBEXT) -o -e /usr/lib64/libvdeplug.$(LIBEXT) \); then echo vde; fi))
# Provide support for vde networking
NETWORK_CCDEFS += -DUSE_VDE_NETWORK
NETWORK_LDFLAGS += -lvdeplug
endif
ifeq (tuntap,$(shell if $(TEST) -e /usr/include/linux/if_tun.h; then echo tuntap; fi)) ifeq (tuntap,$(shell if $(TEST) -e /usr/include/linux/if_tun.h; then echo tuntap; fi))
# Provide support for Tap networking on Linux # Provide support for Tap networking on Linux
NETWORK_TAP_CCDEFS = -DUSE_TAP_NETWORK NETWORK_CCDEFS += -DUSE_TAP_NETWORK
endif endif
ifeq (bsdtuntap,$(shell if $(TEST) -e /usr/include/net/if_tun.h; then echo bsdtuntap; fi)) ifeq (bsdtuntap,$(shell if $(TEST) -e /usr/include/net/if_tun.h -o -e /Library/Extensions/tap.kext; then echo bsdtuntap; fi))
# Provide support for Tap networking # Provide support for Tap networking
NETWORK_TAP_CCDEFS = -DUSE_TAP_NETWORK -DUSE_BSDTUNTAP NETWORK_CCDEFS += -DUSE_TAP_NETWORK -DUSE_BSDTUNTAP
endif endif
ifneq (binexists,$(shell if $(TEST) -e BIN; then echo binexists; fi)) ifneq (binexists,$(shell if $(TEST) -e BIN; then echo binexists; fi))
MKDIRBIN = if $(TEST) ! -e BIN; then mkdir BIN; fi MKDIRBIN = if $(TEST) ! -e BIN; then mkdir BIN; fi
@ -79,6 +96,7 @@ ifeq ($(WIN32),)
endif endif
else else
#Win32 Environments (via MinGW32) #Win32 Environments (via MinGW32)
GCC = gcc
GCC_Path := $(dir $(shell where gcc.exe)) GCC_Path := $(dir $(shell where gcc.exe))
ifeq ($(NOASYNCH),) ifeq ($(NOASYNCH),)
ifeq (pthreads,$(shell if exist ..\pthreads\Pre-built.2\include\pthread.h echo pthreads)) ifeq (pthreads,$(shell if exist ..\pthreads\Pre-built.2\include\pthread.h echo pthreads))
@ -119,7 +137,7 @@ else
endif endif
CC = gcc -std=c99 -U__STRICT_ANSI__ -g -I . $(NETWORK_CCDEFS) $(NETWORK_TAP_CCDEFS) $(OS_CCDEFS) $(ROMS_OPT) CC = $(GCC) -std=c99 -U__STRICT_ANSI__ -g -I . $(NETWORK_CCDEFS) $(OS_CCDEFS) $(ROMS_OPT)
LDFLAGS = $(OS_LDFLAGS) $(NETWORK_LDFLAGS) LDFLAGS = $(OS_LDFLAGS) $(NETWORK_LDFLAGS)
# #

105
scp.c
View file

@ -23,6 +23,17 @@
used in advertising or otherwise to promote the sale, use or other dealings used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik. in this Software without prior written authorization from Robert M Supnik.
25-Sep-11 MP Added the ability for a simulator built with
SIM_ASYNCH_IO to change whether I/O is actually done
asynchronously by the new scp command SET ASYNCH and
SET NOASYNCH
22-Sep-11 MP Added signal catching of SIGHUP and SIGTERM to cause
simulator STOP. This allows an externally signalled
event (i.e. system shutdown, or logoff) to signal a
running simulator of these events and to allow
reasonable actions to be taken. This will facilitate
running a simulator as a 'service' on *nix platforms,
given a sufficiently flexible simulator .ini file.
20-Apr-11 MP Added expansion of %STATUS% and %TSTATUS% in do command 20-Apr-11 MP Added expansion of %STATUS% and %TSTATUS% in do command
arguments. STATUS is the numeric value of the last arguments. STATUS is the numeric value of the last
command error status and TSTATUS is the text message command error status and TSTATUS is the text message
@ -271,6 +282,7 @@ pthread_mutex_t sim_asynch_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t sim_asynch_wake = PTHREAD_COND_INITIALIZER; pthread_cond_t sim_asynch_wake = PTHREAD_COND_INITIALIZER;
pthread_t sim_asynch_main_threadid; pthread_t sim_asynch_main_threadid;
struct sim_unit *sim_asynch_queue = NULL; struct sim_unit *sim_asynch_queue = NULL;
t_bool sim_asynch_enabled = TRUE;
int32 sim_asynch_check; int32 sim_asynch_check;
int32 sim_asynch_latency = 4000; /* 4 usec interrupt latency */ int32 sim_asynch_latency = 4000; /* 4 usec interrupt latency */
int32 sim_asynch_inst_latency = 20; /* assume 5 mip simulator */ int32 sim_asynch_inst_latency = 20; /* assume 5 mip simulator */
@ -387,6 +399,7 @@ t_stat dep_addr (int32 flag, char *cptr, t_addr addr, DEVICE *dptr,
t_stat step_svc (UNIT *ptr); t_stat step_svc (UNIT *ptr);
void sub_args (char *instr, char *tmpbuf, int32 maxstr, char *do_arg[]); void sub_args (char *instr, char *tmpbuf, int32 maxstr, char *do_arg[]);
t_stat set_on (int32 flag, char *cptr); t_stat set_on (int32 flag, char *cptr);
t_stat set_asynch (int32 flag, char *cptr);
/* Global data */ /* Global data */
@ -603,8 +616,11 @@ static CTAB cmd_table[] = {
"set console NODEBUG disable console debugging\n" "set console NODEBUG disable console debugging\n"
"set break <list> set breakpoints\n" "set break <list> set breakpoints\n"
"set nobreak <list> clear breakpoints\n" "set nobreak <list> clear breakpoints\n"
"set throttle x{M|K|%%} set simulation rate\n" "set throttle {x{M|K|%}}|{x/t}\n"
" set simulation rate\n"
"set nothrottle set simulation rate to maximum\n" "set nothrottle set simulation rate to maximum\n"
"set asynch enable asynchronous I/O\n"
"set noasynch disable asynchronous I/O\n"
"set <dev> OCT|DEC|HEX set device display radix\n" "set <dev> OCT|DEC|HEX set device display radix\n"
"set <dev> ENABLED enable device\n" "set <dev> ENABLED enable device\n"
"set <dev> DISABLED disable device\n" "set <dev> DISABLED disable device\n"
@ -628,6 +644,7 @@ static CTAB cmd_table[] = {
"sh{ow} q{ueue} show event queue\n" "sh{ow} q{ueue} show event queue\n"
"sh{ow} ti{me} show simulated time\n" "sh{ow} ti{me} show simulated time\n"
"sh{ow} th{rottle} show simulation rate\n" "sh{ow} th{rottle} show simulation rate\n"
"sh{ow} a{synch} show asynchronouse I/O state\n"
"sh{ow} ve{rsion} show simulator version\n" "sh{ow} ve{rsion} show simulator version\n"
"sh{ow} <dev> RADIX show device display radix\n" "sh{ow} <dev> RADIX show device display radix\n"
"sh{ow} <dev> DEBUG show device debug flags\n" "sh{ow} <dev> DEBUG show device debug flags\n"
@ -1006,13 +1023,6 @@ do {
(cmdp->action != &on_cmd) && (cmdp->action != &on_cmd) &&
(cmdp->action != &echo_cmd))) (cmdp->action != &echo_cmd)))
sim_last_cmd_stat = stat; /* save command error status */ sim_last_cmd_stat = stat; /* save command error status */
staying = (stat != SCPE_EXIT) && /* decide if staying */
(stat != SCPE_AFAIL) &&
(!errabort || (stat < SCPE_BASE) || (stat == SCPE_STEP));
if ((stat == SCPE_AFAIL) && /* handle special case AFAIL */
sim_on_check[sim_do_depth] && /* and use trap action if defined */
sim_on_actions[sim_do_depth][stat]) /* otherwise exit */
staying = TRUE;
if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) && /* error from cmd? */ if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) && /* error from cmd? */
(stat != SCPE_STEP)) { (stat != SCPE_STEP)) {
if (!echo && !sim_quiet && /* report if not echoing */ if (!echo && !sim_quiet && /* report if not echoing */
@ -1023,8 +1033,23 @@ do {
} }
stat = stat & ~SCPE_DOFAILED; /* remove possible flag */ stat = stat & ~SCPE_DOFAILED; /* remove possible flag */
} }
switch (stat) {
case SCPE_OK:
case SCPE_STEP:
break;
case SCPE_AFAIL:
staying = (sim_on_check[sim_do_depth] && /* if trap action defined */
sim_on_actions[sim_do_depth][stat]); /* use it, otherwise exit */
break;
case SCPE_EXIT:
staying = FALSE;
break;
default:
staying = sim_on_check[sim_do_depth];
break;
}
if ((staying || !interactive) && /* report error if staying */ if ((staying || !interactive) && /* report error if staying */
(stat >= SCPE_BASE)) { /* or in cmdline file */ (stat >= SCPE_BASE) && !isdo) { /* or in cmdline file */
printf ("%s\n", sim_error_text (stat)); printf ("%s\n", sim_error_text (stat));
if (sim_log) if (sim_log)
fprintf (sim_log, "%s\n", sim_error_text (stat)); fprintf (sim_log, "%s\n", sim_error_text (stat));
@ -1033,7 +1058,7 @@ do {
(sim_on_check[sim_do_depth]) && (sim_on_check[sim_do_depth]) &&
(stat != SCPE_OK) && (stat != SCPE_OK) &&
(stat != SCPE_STEP)) (stat != SCPE_STEP))
if (sim_on_actions[sim_do_depth][stat]) if ((stat <= SCPE_MAX_ERR) && sim_on_actions[sim_do_depth][stat])
sim_brk_act[sim_do_depth] = sim_on_actions[sim_do_depth][stat]; sim_brk_act[sim_do_depth] = sim_on_actions[sim_do_depth][stat];
else else
sim_brk_act[sim_do_depth] = sim_on_actions[sim_do_depth][0]; sim_brk_act[sim_do_depth] = sim_on_actions[sim_do_depth][0];
@ -1281,6 +1306,61 @@ if ((sim_do_depth != 0) &&
return SCPE_OK; return SCPE_OK;
} }
/* Set asynch/noasynch routine */
t_stat sim_set_asynch (int32 flag, char *cptr)
{
if (cptr && (*cptr != 0)) /* now eol? */
return SCPE_2MARG;
#ifdef SIM_ASYNCH_IO
if (flag == sim_asynch_enabled) /* already set correctly? */
return SCPE_OK;
sim_asynch_enabled = flag;
if (1) {
uint32 i, j;
DEVICE *dptr;
UNIT *uptr;
/* Call unit flush routines to report asynch status change to device layer */
for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* flush attached files */
for (j = 0; j < dptr->numunits; j++) { /* if not buffered in mem */
uptr = dptr->units + j;
if ((uptr->flags & UNIT_ATT) && /* attached, */
!(uptr->flags & UNIT_BUF) && /* not buffered, */
(uptr->fileref)) /* real file, */
if (uptr->io_flush) /* unit specific flush routine */
uptr->io_flush (uptr);
}
}
}
if (!sim_quiet)
printf ("Asynchronous I/O %sabled\n", sim_asynch_enabled ? "en" : "dis");
if (sim_log)
fprintf (sim_log, "Asynchronous I/O %sabled\n", sim_asynch_enabled ? "en" : "dis");
return SCPE_OK;
#else
if (!sim_quiet)
printf ("Asynchronous I/O is not available in this simulator\n");
if (sim_log)
fprintf (sim_log, "Asynchronous I/O is not available in this simulator\n");
return SCPE_NOFNC;
#endif
}
/* Show asynch routine */
t_stat sim_show_asynch (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
{
if (cptr && (*cptr != 0))
return SCPE_2MARG;
#ifdef SIM_ASYNCH_IO
fprintf (st, "Asynchronous I/O is %sabled\n", (sim_asynch_enabled) ? "en" : "dis");
#else
fprintf (st, "Asynchronous I/O is not available in this simulator\n");
#endif
return SCPE_OK;
}
/* Set command */ /* Set command */
t_stat set_cmd (int32 flag, char *cptr) t_stat set_cmd (int32 flag, char *cptr)
@ -1305,6 +1385,8 @@ static CTAB set_glob_tab[] = {
{ "NODEBUG", &sim_set_deboff, 0 }, /* deprecated */ { "NODEBUG", &sim_set_deboff, 0 }, /* deprecated */
{ "THROTTLE", &sim_set_throt, 1 }, { "THROTTLE", &sim_set_throt, 1 },
{ "NOTHROTTLE", &sim_set_throt, 0 }, { "NOTHROTTLE", &sim_set_throt, 0 },
{ "ASYNCH", &sim_set_asynch, 1 },
{ "NOASYNCH", &sim_set_asynch, 0 },
{ "ON", &set_on, 1 }, { "ON", &set_on, 1 },
{ "NOON", &set_on, 0 }, { "NOON", &set_on, 0 },
{ NULL, NULL, 0 } { NULL, NULL, 0 }
@ -1570,6 +1652,7 @@ static SHTAB show_glob_tab[] = {
{ "TELNET", &sim_show_telnet, 0 }, /* deprecated */ { "TELNET", &sim_show_telnet, 0 }, /* deprecated */
{ "DEBUG", &sim_show_debug, 0 }, /* deprecated */ { "DEBUG", &sim_show_debug, 0 }, /* deprecated */
{ "THROTTLE", &sim_show_throt, 0 }, { "THROTTLE", &sim_show_throt, 0 },
{ "ASYNCH", &sim_show_asynch, 0 },
{ "ON", &show_on, 0 }, { "ON", &show_on, 0 },
{ NULL, NULL, 0 } { NULL, NULL, 0 }
}; };
@ -2879,6 +2962,8 @@ for (j=0, r = SCPE_OK; j<attcnt; j++) {
dptr = find_dev_from_unit (attunits[j]); dptr = find_dev_from_unit (attunits[j]);
sim_switches = attswitches[j]; sim_switches = attswitches[j];
r = scp_attach_unit (dptr, attunits[j], attnames[j]);/* reattach unit */ r = scp_attach_unit (dptr, attunits[j], attnames[j]);/* reattach unit */
if (r != SCPE_OK)
printf ("Error Attaching %s to %s\n", sim_dname (dptr), attnames[j]);
} }
free (attnames[j]); free (attnames[j]);
} }

View file

@ -130,7 +130,7 @@ fprintf (iFile, "#define ROM_%s_H 0\n", rom_array_name);
fprintf (iFile, "/*\n"); fprintf (iFile, "/*\n");
fprintf (iFile, " %s produced at %s", include_filename, ctime(&now)); fprintf (iFile, " %s produced at %s", include_filename, ctime(&now));
fprintf (iFile, " from %s which was last modified at %s", rom_filename, ctime(&statb.st_mtime)); fprintf (iFile, " from %s which was last modified at %s", rom_filename, ctime(&statb.st_mtime));
fprintf (iFile, " file size: %d (0x%X)\n", statb.st_size, statb.st_size); fprintf (iFile, " file size: %d (0x%X)\n", (int)statb.st_size, (int)statb.st_size);
fprintf (iFile, "*/\n"); fprintf (iFile, "*/\n");
fprintf (iFile, "unsigned char %s[] = {", rom_array_name); fprintf (iFile, "unsigned char %s[] = {", rom_array_name);
for (bytes_written=0;bytes_written<statb.st_size; ++bytes_written) { for (bytes_written=0;bytes_written<statb.st_size; ++bytes_written) {

View file

@ -424,7 +424,8 @@ while (*cptr != 0) { /* do all mods */
*cvptr++ = 0; *cvptr++ = 0;
get_glyph (gbuf, gbuf, 0); /* modifier to UC */ get_glyph (gbuf, gbuf, 0); /* modifier to UC */
if (isdigit (*gbuf)) { if (isdigit (*gbuf)) {
if (sim_con_tmxr.master) return SCPE_ALATT; /* already open? */ if (sim_con_tmxr.master) /* already open? */
sim_set_notelnet (0, NULL); /* close first */
return tmxr_open_master (&sim_con_tmxr, gbuf); /* open master socket */ return tmxr_open_master (&sim_con_tmxr, gbuf); /* open master socket */
} }
else else
@ -633,8 +634,12 @@ if (sim_con_ldsc.conn || sim_con_ldsc.txbfd) { /* connected or buffered
tmxr_poll_rx (&sim_con_tmxr); /* poll (check disconn) */ tmxr_poll_rx (&sim_con_tmxr); /* poll (check disconn) */
if (sim_con_ldsc.conn || sim_con_ldsc.txbfd) { /* still connected? */ if (sim_con_ldsc.conn || sim_con_ldsc.txbfd) { /* still connected? */
if (!sim_con_ldsc.conn) { if (!sim_con_ldsc.conn) {
printf ("Running with Buffered Console\n"); /* print transition */ printf ("Running with Buffered Console\r\n"); /* print transition */
fflush (stdout); fflush (stdout);
if (sim_log) { /* log file? */
fprintf (sim_log, "Running with Buffered Console\n");
fflush (sim_log);
}
} }
return SCPE_OK; return SCPE_OK;
} }
@ -643,8 +648,12 @@ for (i = 0; i < sec; i++) { /* loop */
if (tmxr_poll_conn (&sim_con_tmxr) >= 0) { /* poll connect */ if (tmxr_poll_conn (&sim_con_tmxr) >= 0) { /* poll connect */
sim_con_ldsc.rcve = 1; /* rcv enabled */ sim_con_ldsc.rcve = 1; /* rcv enabled */
if (i) { /* if delayed */ if (i) { /* if delayed */
printf ("Running\n"); /* print transition */ printf ("Running\r\n"); /* print transition */
fflush (stdout); fflush (stdout);
if (sim_log) { /* log file? */
fprintf (sim_log, "Running\n");
fflush (sim_log);
}
} }
return SCPE_OK; /* ready to proceed */ return SCPE_OK; /* ready to proceed */
} }
@ -652,8 +661,12 @@ for (i = 0; i < sec; i++) { /* loop */
if ((c == SCPE_STOP) || stop_cpu) if ((c == SCPE_STOP) || stop_cpu)
return SCPE_STOP; return SCPE_STOP;
if ((i % 10) == 0) { /* Status every 10 sec */ if ((i % 10) == 0) { /* Status every 10 sec */
printf ("Waiting for console Telnet connection\n"); printf ("Waiting for console Telnet connection\r\n");
fflush (stdout); fflush (stdout);
if (sim_log) { /* log file? */
fprintf (sim_log, "Waiting for console Telnet connection\n");
fflush (sim_log);
}
} }
sim_os_sleep (1); /* wait 1 second */ sim_os_sleep (1); /* wait 1 second */
} }

View file

@ -565,6 +565,7 @@ extern pthread_cond_t sim_asynch_wake;
extern pthread_t sim_asynch_main_threadid; extern pthread_t sim_asynch_main_threadid;
extern struct sim_unit *sim_asynch_queue; extern struct sim_unit *sim_asynch_queue;
extern t_bool sim_idle_wait; extern t_bool sim_idle_wait;
extern t_bool sim_asynch_enabled;
extern int32 sim_asynch_check; extern int32 sim_asynch_check;
extern int32 sim_asynch_latency; extern int32 sim_asynch_latency;
extern int32 sim_asynch_inst_latency; extern int32 sim_asynch_inst_latency;

View file

@ -408,8 +408,9 @@ return SCPE_NOFNC;
struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
pthread_attr_t attr; pthread_attr_t attr;
ctx->asynch_io = 1; ctx->asynch_io = sim_asynch_enabled;
ctx->asynch_io_latency = latency; ctx->asynch_io_latency = latency;
if (ctx->asynch_io) {
pthread_mutex_init (&ctx->io_lock, NULL); pthread_mutex_init (&ctx->io_lock, NULL);
pthread_cond_init (&ctx->io_cond, NULL); pthread_cond_init (&ctx->io_cond, NULL);
pthread_attr_init(&attr); pthread_attr_init(&attr);
@ -417,6 +418,7 @@ pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_create (&ctx->io_thread, &attr, _disk_io, (void *)uptr); pthread_create (&ctx->io_thread, &attr, _disk_io, (void *)uptr);
pthread_attr_destroy(&attr); pthread_attr_destroy(&attr);
uptr->a_check_completion = _disk_completion_dispatch; uptr->a_check_completion = _disk_completion_dispatch;
}
#endif #endif
return SCPE_OK; return SCPE_OK;
} }
@ -723,6 +725,7 @@ uint32 f = DK_GET_FMT (uptr);
#if defined (SIM_ASYNCH_IO) #if defined (SIM_ASYNCH_IO)
sim_disk_clr_async (uptr); sim_disk_clr_async (uptr);
if (sim_asynch_enabled)
sim_disk_set_async (uptr, 0); sim_disk_set_async (uptr, 0);
#endif #endif
switch (f) { /* case on format */ switch (f) { /* case on format */
@ -1332,7 +1335,7 @@ if (strchr (openmode, 'r'))
DesiredAccess |= GENERIC_READ; DesiredAccess |= GENERIC_READ;
if (strchr (openmode, 'w') || strchr (openmode, '+')) if (strchr (openmode, 'w') || strchr (openmode, '+'))
DesiredAccess |= GENERIC_WRITE; DesiredAccess |= GENERIC_WRITE;
Handle = CreateFileA (rawdevicename, DesiredAccess, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); Handle = CreateFileA (rawdevicename, DesiredAccess, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS|FILE_FLAG_WRITE_THROUGH, NULL);
if (Handle == INVALID_HANDLE_VALUE) { if (Handle == INVALID_HANDLE_VALUE) {
_set_errno_from_status (GetLastError ()); _set_errno_from_status (GetLastError ());
return NULL; return NULL;
@ -1418,7 +1421,10 @@ static t_stat sim_os_disk_unload_raw (FILE *Disk)
{ {
#ifdef IOCTL_STORAGE_EJECT_MEDIA #ifdef IOCTL_STORAGE_EJECT_MEDIA
DWORD BytesReturned; DWORD BytesReturned;
uint32 Removable = FALSE;
sim_os_disk_info_raw (Disk, NULL, &Removable);
if (Removable) {
if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */ if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */
IOCTL_STORAGE_EJECT_MEDIA, /* dwIoControlCode */ IOCTL_STORAGE_EJECT_MEDIA, /* dwIoControlCode */
NULL, /* lpInBuffer */ NULL, /* lpInBuffer */
@ -1430,6 +1436,7 @@ if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */
_set_errno_from_status (GetLastError ()); _set_errno_from_status (GetLastError ());
return SCPE_IOERR; return SCPE_IOERR;
} }
}
return SCPE_OK; return SCPE_OK;
#else #else
return SCPE_NOFNC; return SCPE_NOFNC;
@ -1440,7 +1447,10 @@ static t_bool sim_os_disk_isavailable_raw (FILE *Disk)
{ {
#ifdef IOCTL_STORAGE_EJECT_MEDIA #ifdef IOCTL_STORAGE_EJECT_MEDIA
DWORD BytesReturned; DWORD BytesReturned;
uint32 Removable = FALSE;
sim_os_disk_info_raw (Disk, NULL, &Removable);
if (Removable) {
if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */ if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */
IOCTL_STORAGE_CHECK_VERIFY, /* dwIoControlCode */ IOCTL_STORAGE_CHECK_VERIFY, /* dwIoControlCode */
NULL, /* lpInBuffer */ NULL, /* lpInBuffer */
@ -1452,6 +1462,7 @@ if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */
_set_errno_from_status (GetLastError ()); _set_errno_from_status (GetLastError ());
return FALSE; return FALSE;
} }
}
#endif #endif
return TRUE; return TRUE;
} }
@ -2646,6 +2657,7 @@ if (SizeInBytes > ((uint64)(1024*1024*1024))*2040) {
} }
if (File = sim_fopen (szVHDPath, "rb")) { if (File = sim_fopen (szVHDPath, "rb")) {
fclose (File); fclose (File);
File = NULL;
Status = EEXIST; Status = EEXIST;
goto Cleanup_Return; goto Cleanup_Return;
} }
@ -2773,6 +2785,7 @@ if (WriteFilePosition(File,
Cleanup_Return: Cleanup_Return:
free (BAT); free (BAT);
if (File)
fclose (File); fclose (File);
if (Status) { if (Status) {
if (Status != EEXIST) if (Status != EEXIST)

View file

@ -138,6 +138,15 @@
at open time. This functionality is only useful/needed at open time. This functionality is only useful/needed
on *nix platforms since native sharing of Windows NIC on *nix platforms since native sharing of Windows NIC
devices works with no external magic. devices works with no external magic.
USE_VDE_NETWORK - Specifies that support for vde networking should be
included. This can be leveraged, along with OS bridging
capabilities to share a single LAN interface. It also
can allow a simulator to have useful networking
functionality when running without root access. This
allows device names of the form vde:/tmp/switch to be
specified at open time. This functionality is only
available on *nix platforms since the vde api isn't
available on Windows.
NEED_PCAP_SENDPACKET NEED_PCAP_SENDPACKET
- Specifies that you are using an older version of libpcap - Specifies that you are using an older version of libpcap
@ -151,6 +160,11 @@
Modification history: Modification history:
30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking
29-Oct-11 MP Added support for integrated Tap networking interfaces on OSX
12-Aug-11 MP Cleaned up payload length determination
Fixed race condition detecting reflections when threaded
reading and writing is enabled
18-Apr-11 MP Fixed race condition with self loopback packets in 18-Apr-11 MP Fixed race condition with self loopback packets in
multithreaded environments multithreaded environments
09-Jan-11 MP Fixed missing crc data when USE_READER_THREAD is defined and 09-Jan-11 MP Fixed missing crc data when USE_READER_THREAD is defined and
@ -787,12 +801,16 @@ void eth_show_dev (FILE* st, ETH_DEV* dev)
#elif defined(USE_BSDTUNTAP) #elif defined(USE_BSDTUNTAP)
#include <sys/types.h> #include <sys/types.h>
#include <net/if_types.h> #include <net/if_types.h>
#include <net/if_tun.h> #include <net/if.h>
#else /* We don't know how to do this on the current platform */ #else /* We don't know how to do this on the current platform */
#undef USE_TAP_NETWORK #undef USE_TAP_NETWORK
#endif #endif
#endif /* USE_TAP_NETWORK */ #endif /* USE_TAP_NETWORK */
#ifdef USE_VDE_NETWORK
#include <libvdeplug.h>
#endif /* USE_VDE_NETWORK */
/* Allows windows to look up user-defined adapter names */ /* Allows windows to look up user-defined adapter names */
#if defined(_WIN32) #if defined(_WIN32)
#include <winreg.h> #include <winreg.h>
@ -1092,10 +1110,12 @@ static void eth_get_nic_hw_addr(ETH_DEV* dev, char *devname)
if (!pcap_mac_if_win32(devname, (UCHAR *)&dev->host_nic_phy_hw_addr)) if (!pcap_mac_if_win32(devname, (UCHAR *)&dev->host_nic_phy_hw_addr))
dev->have_host_nic_phy_addr = 1; dev->have_host_nic_phy_addr = 1;
#elif !defined (__VMS) #elif !defined (__VMS)
{ if (1) {
char command[1024]; char command[1024];
FILE *f; FILE *f;
if (0 == strncmp("vde:", devname, 4))
return;
memset(command, 0, sizeof(command)); 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); 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); system(command);
@ -1148,16 +1168,24 @@ int sched_policy;
struct sched_param sched_priority; struct sched_param sched_priority;
#if defined (_WIN32) #if defined (_WIN32)
HANDLE hWait = pcap_getevent ((pcap_t*)dev->handle); HANDLE hWait = pcap_getevent ((pcap_t*)dev->handle);
#endif #else
#if defined (MUST_DO_SELECT)
struct timeval timeout;
int sel_ret; int sel_ret;
int pcap_fd; int do_select = 0;
int select_fd;
if (dev->pcap_mode) switch (dev->eth_api) {
pcap_fd = pcap_get_selectable_fd((pcap_t *)dev->handle); case ETH_API_PCAP:
else #if defined (MUST_DO_SELECT)
pcap_fd = dev->fd_handle; do_select = 1;
select_fd = pcap_get_selectable_fd((pcap_t *)dev->handle);
#endif
break;
case ETH_API_TAP:
case ETH_API_VDE:
do_select = 1;
select_fd = dev->fd_handle;
break;
}
#endif #endif
sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n"); sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n");
@ -1169,32 +1197,35 @@ else
++sched_priority.sched_priority; ++sched_priority.sched_priority;
pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); pthread_setschedparam (pthread_self(), sched_policy, &sched_priority);
while ((dev->pcap_mode && dev->handle) || dev->fd_handle) { while (dev->handle) {
#if defined (MUST_DO_SELECT)
fd_set setl;
FD_ZERO(&setl);
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) {
#else /* !MUST_DO_SELECT */
#if defined (_WIN32) #if defined (_WIN32)
if (WAIT_OBJECT_0 == WaitForSingleObject (hWait, 250)) { if (WAIT_OBJECT_0 == WaitForSingleObject (hWait, 250)) {
#else #else
if (1) { fd_set setl;
#endif struct timeval timeout;
#endif /* MUST_DO_SELECT */
if ((dev->pcap_mode) && (!dev->handle)) if (do_select) {
break; FD_ZERO(&setl);
if ((!dev->pcap_mode) && (!dev->fd_handle)) FD_SET(select_fd, &setl);
timeout.tv_sec = 0;
timeout.tv_usec = 250*1000;
sel_ret = select(1+select_fd, &setl, NULL, NULL, &timeout);
}
else
sel_ret = 1;
if (sel_ret < 0 && errno != EINTR) break;
if (sel_ret > 0) {
#endif /* _WIN32 */
if (!dev->handle)
break; break;
/* dispatch read request queue available packets */ /* dispatch read request queue available packets */
if (dev->pcap_mode) switch (dev->eth_api) {
case ETH_API_PCAP:
status = pcap_dispatch ((pcap_t*)dev->handle, -1, &_eth_callback, (u_char*)dev); status = pcap_dispatch ((pcap_t*)dev->handle, -1, &_eth_callback, (u_char*)dev);
break;
#ifdef USE_TAP_NETWORK #ifdef USE_TAP_NETWORK
else { case ETH_API_TAP:
if (1) {
struct pcap_pkthdr header; struct pcap_pkthdr header;
int len; int len;
u_char buf[ETH_MAX_JUMBO_FRAME]; u_char buf[ETH_MAX_JUMBO_FRAME];
@ -1205,14 +1236,35 @@ else
status = 1; status = 1;
header.caplen = header.len = len; header.caplen = header.len = len;
_eth_callback((u_char *)dev, &header, buf); _eth_callback((u_char *)dev, &header, buf);
} else { }
else
status = 0; status = 0;
} }
} break;
#endif /* USE_TAP_NETWORK */ #endif /* USE_TAP_NETWORK */
#ifdef USE_VDE_NETWORK
case ETH_API_VDE:
if (1) {
struct pcap_pkthdr header;
int len;
u_char buf[ETH_MAX_JUMBO_FRAME];
memset(&header, 0, sizeof(header));
len = vde_recv((VDECONN *)dev->handle, buf, sizeof(buf), 0);
if (len > 0) {
status = 1;
header.caplen = header.len = len;
_eth_callback((u_char *)dev, &header, buf);
}
else
status = 0;
}
break;
#endif /* USE_VDE_NETWORK */
}
if ((status > 0) && (dev->asynch_io)) { if ((status > 0) && (dev->asynch_io)) {
int wakeup_needed; int wakeup_needed;
pthread_mutex_lock (&dev->lock); pthread_mutex_lock (&dev->lock);
wakeup_needed = (dev->read_queue.count != 0); wakeup_needed = (dev->read_queue.count != 0);
pthread_mutex_unlock (&dev->lock); pthread_mutex_unlock (&dev->lock);
@ -1246,7 +1298,7 @@ struct sched_param sched_priority;
sim_debug(dev->dbit, dev->dptr, "Writer Thread Starting\n"); sim_debug(dev->dbit, dev->dptr, "Writer Thread Starting\n");
pthread_mutex_lock (&dev->writer_lock); pthread_mutex_lock (&dev->writer_lock);
while ((dev->pcap_mode && dev->handle) || dev->fd_handle) { while (dev->handle) {
pthread_cond_wait (&dev->writer_cond, &dev->writer_lock); pthread_cond_wait (&dev->writer_cond, &dev->writer_lock);
while (request = dev->write_requests) { while (request = dev->write_requests) {
/* Pull buffer off request list */ /* Pull buffer off request list */
@ -1330,7 +1382,8 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit)
savname = eth_getname(num, temp); savname = eth_getname(num, temp);
if (savname == 0) /* didn't translate */ if (savname == 0) /* didn't translate */
return SCPE_OPENERR; return SCPE_OPENERR;
} else { }
else {
/* are they trying to use device description? */ /* are they trying to use device description? */
savname = eth_getname_bydesc(name, temp); savname = eth_getname_bydesc(name, temp);
if (savname == 0) { /* didn't translate */ if (savname == 0) { /* didn't translate */
@ -1343,16 +1396,7 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit)
/* attempt to connect device */ /* attempt to connect device */
memset(errbuf, 0, sizeof(errbuf)); memset(errbuf, 0, sizeof(errbuf));
if (strncmp("tap:", savname, 4)) { if (0 == 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 tun = -1; /* TUN/TAP Socket */
int on = 1; int on = 1;
char dev_name[64] = ""; char dev_name[64] = "";
@ -1374,18 +1418,21 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit)
strcpy(ifr.ifr_name, savname+4); strcpy(ifr.ifr_name, savname+4);
ifr.ifr_flags = IFF_TAP|IFF_NO_PI; ifr.ifr_flags = IFF_TAP|IFF_NO_PI;
// Send interface requests to TUN/TAP driver. /* Send interface requests to TUN/TAP driver. */
if (ioctl(tun, TUNSETIFF, &ifr) >= 0) { if (ioctl(tun, TUNSETIFF, &ifr) >= 0) {
if (ioctl(tun, FIONBIO, &on)) { if (ioctl(tun, FIONBIO, &on)) {
strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); strncpy(errbuf, strerror(errno), sizeof(errbuf)-1);
close(tun); close(tun);
} else { }
else {
dev->fd_handle = tun; dev->fd_handle = tun;
strcpy(savname, ifr.ifr_name); strcpy(savname, ifr.ifr_name);
} }
} else }
else
strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); strncpy(errbuf, strerror(errno), sizeof(errbuf)-1);
} else }
else
strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); strncpy(errbuf, strerror(errno), sizeof(errbuf)-1);
#elif defined(USE_BSDTUNTAP) && defined(USE_TAP_NETWORK) #elif defined(USE_BSDTUNTAP) && defined(USE_TAP_NETWORK)
snprintf(dev_name, sizeof(dev_name)-1, "/dev/%s", savname+4); snprintf(dev_name, sizeof(dev_name)-1, "/dev/%s", savname+4);
@ -1395,16 +1442,70 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit)
if (ioctl(tun, FIONBIO, &on)) { if (ioctl(tun, FIONBIO, &on)) {
strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); strncpy(errbuf, strerror(errno), sizeof(errbuf)-1);
close(tun); close(tun);
} else { }
else {
dev->fd_handle = tun; dev->fd_handle = tun;
strcpy(savname, savname+4); strcpy(savname, savname+4);
} }
} else { #if defined (__APPLE__)
strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); if (1) {
struct ifreq ifr;
int s;
memset (&ifr, 0, sizeof(ifr));
ifr.ifr_addr.sa_family = AF_INET;
strncpy(ifr.ifr_name, savname+4, sizeof(ifr.ifr_name));
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) >= 0) {
ifr.ifr_flags |= IFF_UP;
ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr);
} }
close(s);
}
}
#endif
}
else
strncpy(errbuf, strerror(errno), sizeof(errbuf)-1);
#else #else
strncpy(errbuf, "No support for tap: devices", sizeof(errbuf)-1); strncpy(errbuf, "No support for tap: devices", sizeof(errbuf)-1);
#endif /* !defined(__linux) && !defined(USE_BSDTUNTAP) */ #endif /* !defined(__linux) && !defined(USE_BSDTUNTAP) */
if (0 == errbuf[0]) {
dev->eth_api = ETH_API_TAP;
dev->handle = (void *)1; /* Flag used to indicated open */
}
}
else
if (0 == strncmp("vde:", savname, 4)) {
#if defined(USE_VDE_NETWORK)
struct vde_open_args voa;
memset(&voa, 0, sizeof(voa));
if (!strcmp(savname, "vde:vdedevice")) {
msg = "Eth: Must specify actual vde device name (i.e. vde:/tmp/switch)\r\n";
printf (msg, errbuf);
if (sim_log) fprintf (sim_log, msg, errbuf);
return SCPE_OPENERR;
}
if (!(dev->handle = (void*) vde_open(savname+4, "simh", &voa)))
strncpy(errbuf, strerror(errno), sizeof(errbuf)-1);
else {
dev->eth_api = ETH_API_VDE;
dev->fd_handle = vde_datafd((VDECONN*)dev->handle);
}
#else
strncpy(errbuf, "No support for vde: network devices", sizeof(errbuf)-1);
#endif /* !defined(__linux) && !defined(USE_BSDTUNTAP) */
}
else {
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->eth_api = ETH_API_PCAP;
} }
if (errbuf[0]) { if (errbuf[0]) {
msg = "Eth: open error - %s\r\n"; msg = "Eth: open error - %s\r\n";
@ -1430,14 +1531,14 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit)
#if !defined(HAS_PCAP_SENDPACKET) && defined (xBSD) && !defined (__APPLE__) #if !defined(HAS_PCAP_SENDPACKET) && defined (xBSD) && !defined (__APPLE__)
/* Tell the kernel that the header is fully-formed when it gets it. /* Tell the kernel that the header is fully-formed when it gets it.
This is required in order to fake the src address. */ This is required in order to fake the src address. */
if (dev->pcap_mode) { if (dev->eth_api == ETH_API_PCAP) {
int one = 1; int one = 1;
ioctl(pcap_fileno(dev->handle), BIOCSHDRCMPLT, &one); ioctl(pcap_fileno(dev->handle), BIOCSHDRCMPLT, &one);
} }
#endif /* xBSD */ #endif /* xBSD */
#if defined (USE_READER_THREAD) #if defined (USE_READER_THREAD)
{ if (1) {
pthread_attr_t attr; pthread_attr_t attr;
#if defined(_WIN32) #if defined(_WIN32)
@ -1457,7 +1558,7 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit)
#else /* !defined (USE_READER_THREAD */ #else /* !defined (USE_READER_THREAD */
#ifdef USE_SETNONBLOCK #ifdef USE_SETNONBLOCK
/* set ethernet device non-blocking so pcap_dispatch() doesn't hang */ /* set ethernet device non-blocking so pcap_dispatch() doesn't hang */
if (dev->pcap_mode && (pcap_setnonblock (dev->handle, 1, errbuf) == -1)) { if ((dev->eth_api == ETH_API_PCAP) && (pcap_setnonblock (dev->handle, 1, errbuf) == -1)) {
msg = "Eth: Failed to set non-blocking: %s\r\n"; msg = "Eth: Failed to set non-blocking: %s\r\n";
printf (msg, errbuf); printf (msg, errbuf);
if (sim_log) fprintf (sim_log, msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf);
@ -1465,7 +1566,7 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit)
#endif #endif
#endif /* !defined (USE_READER_THREAD */ #endif /* !defined (USE_READER_THREAD */
#if defined (__APPLE__) #if defined (__APPLE__)
if (1) { if (dev->eth_api == ETH_API_PCAP) {
/* Deliver packets immediately, needed for OS X 10.6.2 and later /* Deliver packets immediately, needed for OS X 10.6.2 and later
* (Snow-Leopard). * (Snow-Leopard).
* See this thread on libpcap and Mac Os X 10.6 Snow Leopard on * See this thread on libpcap and Mac Os X 10.6 Snow Leopard on
@ -1503,7 +1604,6 @@ t_stat eth_close(ETH_DEV* dev)
pthread_cond_destroy (&dev->writer_cond); pthread_cond_destroy (&dev->writer_cond);
if (1) { if (1) {
struct write_request *buffer; struct write_request *buffer;
while (buffer = dev->write_buffers) { while (buffer = dev->write_buffers) {
dev->write_buffers = buffer->next; dev->write_buffers = buffer->next;
free(buffer); free(buffer);
@ -1516,12 +1616,21 @@ t_stat eth_close(ETH_DEV* dev)
ethq_destroy (&dev->read_queue); /* release FIFO queue */ ethq_destroy (&dev->read_queue); /* release FIFO queue */
#endif #endif
if (dev->pcap_mode) switch (dev->eth_api) {
case ETH_API_PCAP:
pcap_close(pcap); pcap_close(pcap);
break;
#ifdef USE_TAP_NETWORK #ifdef USE_TAP_NETWORK
else case ETH_API_TAP:
close(pcap_fd); close(pcap_fd);
break;
#endif #endif
#ifdef USE_VDE_NETWORK
case ETH_API_VDE:
vde_close((VDECONN*)pcap);
break;
#endif
}
printf (msg, dev->name); printf (msg, dev->name);
if (sim_log) fprintf (sim_log, msg, dev->name); if (sim_log) fprintf (sim_log, msg, dev->name);
@ -1628,13 +1737,28 @@ t_stat _eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
} }
/* dispatch write request (synchronous; no need to save write info to dev) */ /* dispatch write request (synchronous; no need to save write info to dev) */
if (dev->pcap_mode) switch (dev->eth_api) {
case ETH_API_PCAP:
status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len); status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len);
break;
#ifdef USE_TAP_NETWORK #ifdef USE_TAP_NETWORK
else case ETH_API_TAP:
status = ((packet->len == write(dev->fd_handle, (void *)packet->msg, packet->len)) ? 0 : -1); status = ((packet->len == write(dev->fd_handle, (void *)packet->msg, packet->len)) ? 0 : -1);
break;
#endif #endif
#ifdef USE_VDE_NETWORK
case ETH_API_VDE:
status = vde_send((VDECONN*)dev->handle, (void *)packet->msg, packet->len, 0);
if ((status == packet->len) || (status == 0))
status = 0;
else
if ((status == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
status = 0;
else
status = 1;
break;
#endif
}
/* On error, correct loopback bookkeeping */ /* On error, correct loopback bookkeeping */
if ((status != 0) && loopback_self_frame) { if ((status != 0) && loopback_self_frame) {
#ifdef USE_READER_THREAD #ifdef USE_READER_THREAD
@ -1693,7 +1817,8 @@ t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
++write_queue_size; ++write_queue_size;
} }
last_request->next = request; last_request->next = request;
} else }
else
dev->write_requests = request; dev->write_requests = request;
if (write_queue_size > dev->write_queue_peak) if (write_queue_size > dev->write_queue_peak)
dev->write_queue_peak = write_queue_size; dev->write_queue_peak = write_queue_size;
@ -1745,12 +1870,14 @@ _eth_hash_validate(ETH_MAC *MultiCastList, int count, ETH_MULTIHASH hash)
lhash[0], lhash[1], lhash[2], lhash[3], lhash[0], lhash[1], lhash[2], lhash[3],
lhash[4], lhash[5], lhash[6], lhash[7]); lhash[4], lhash[5], lhash[6], lhash[7]);
} }
else {
printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n",
hash[0], hash[1], hash[2], hash[3], hash[0], hash[1], hash[2], hash[3],
hash[4], hash[5], hash[6], hash[7]); hash[4], hash[5], hash[6], hash[7]);
printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n",
lhash[0], lhash[1], lhash[2], lhash[3], lhash[0], lhash[1], lhash[2], lhash[3],
lhash[4], lhash[5], lhash[6], lhash[7]); lhash[4], lhash[5], lhash[6], lhash[7]);
}
return 0; return 0;
} }
@ -1983,7 +2110,8 @@ _eth_fix_ip_jumbo_offload(ETH_DEV* dev, const u_char* msg, int len)
if (payload_len > mtu_payload) { if (payload_len > mtu_payload) {
ip_flags |= IP_MF_FLAG; ip_flags |= IP_MF_FLAG;
IP->total_len = htons(((mtu_payload>>3)<<3) + IP_HLEN(IP)); IP->total_len = htons(((mtu_payload>>3)<<3) + IP_HLEN(IP));
} else { }
else {
IP->total_len = htons(payload_len + IP_HLEN(IP)); IP->total_len = htons(payload_len + IP_HLEN(IP));
} }
IP->flags = htons(ip_flags); IP->flags = htons(ip_flags);
@ -1992,7 +2120,8 @@ _eth_fix_ip_jumbo_offload(ETH_DEV* dev, const u_char* msg, int len)
header.caplen = header.len = 14 + ntohs(IP->total_len); header.caplen = header.len = 14 + ntohs(IP->total_len);
eth_packet_trace (dev, ((u_char *)IP)-14, header.len, "reading Datagram fragment"); eth_packet_trace (dev, ((u_char *)IP)-14, header.len, "reading Datagram fragment");
#if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET #if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET
{ /* Debugging is easier if we read packets directly with pcap if (1) {
/* Debugging is easier if we read packets directly with pcap
(i.e. we can use Wireshark to verify packet contents) (i.e. we can use Wireshark to verify packet contents)
we don't want to do this all the time for 2 reasons: we don't want to do this all the time for 2 reasons:
1) sending through pcap involves kernel transitions and 1) sending through pcap involves kernel transitions and
@ -2033,7 +2162,8 @@ _eth_fix_ip_jumbo_offload(ETH_DEV* dev, const u_char* msg, int len)
if (payload_len > mtu_payload) { if (payload_len > mtu_payload) {
TCP->data_offset_and_flags = htons(orig_tcp_flags&~(TCP_PSH_FLAG|TCP_FIN_FLAG|TCP_RST_FLAG)); TCP->data_offset_and_flags = htons(orig_tcp_flags&~(TCP_PSH_FLAG|TCP_FIN_FLAG|TCP_RST_FLAG));
IP->total_len = htons(mtu_payload + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); IP->total_len = htons(mtu_payload + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP));
} else { }
else {
TCP->data_offset_and_flags = htons(orig_tcp_flags); TCP->data_offset_and_flags = htons(orig_tcp_flags);
IP->total_len = htons(payload_len + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); IP->total_len = htons(payload_len + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP));
} }
@ -2044,7 +2174,8 @@ _eth_fix_ip_jumbo_offload(ETH_DEV* dev, const u_char* msg, int len)
header.caplen = header.len = 14 + ntohs(IP->total_len); header.caplen = header.len = 14 + ntohs(IP->total_len);
eth_packet_trace_ex (dev, ((u_char *)IP)-14, header.len, "reading TCP segment", 1, dev->dbit); 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 #if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET
{ /* Debugging is easier if we read packets directly with pcap if (1) {
/* Debugging is easier if we read packets directly with pcap
(i.e. we can use Wireshark to verify packet contents) (i.e. we can use Wireshark to verify packet contents)
we don't want to do this all the time for 2 reasons: we don't want to do this all the time for 2 reasons:
1) sending through pcap involves kernel transitions and 1) sending through pcap involves kernel transitions and
@ -2138,19 +2269,27 @@ static void
_eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data) _eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data)
{ {
ETH_DEV* dev = (ETH_DEV*) info; ETH_DEV* dev = (ETH_DEV*) info;
#ifdef USE_BPF int to_me;
int to_me = 1; int from_me = 0;
int i;
int bpf_used;
switch (dev->eth_api) {
case ETH_API_PCAP:
#ifdef USE_BPF
bpf_used = 1;
to_me = 1;
/* AUTODIN II hash mode? */ /* AUTODIN II hash mode? */
if ((dev->hash_filter) && (data[0] & 0x01) && (!dev->promiscuous) && (!dev->all_multicast)) if ((dev->hash_filter) && (data[0] & 0x01) && (!dev->promiscuous) && (!dev->all_multicast))
to_me = _eth_hash_lookup(dev->hash, data); to_me = _eth_hash_lookup(dev->hash, data);
#else /* !USE_BPF */ break;
int to_me = 0; #endif /* USE_BPF */
int from_me = 0; case ETH_API_TAP:
int i; case ETH_API_VDE:
bpf_used = 0;
to_me = 0;
eth_packet_trace (dev, data, header->len, "received"); eth_packet_trace (dev, data, header->len, "received");
f
for (i = 0; i < dev->addr_count; i++) { for (i = 0; i < dev->addr_count; i++) {
if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1; 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; if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1;
@ -2165,7 +2304,8 @@ f
/* AUTODIN II hash mode? */ /* AUTODIN II hash mode? */
if ((dev->hash_filter) && (!to_me) && (data[0] & 0x01)) if ((dev->hash_filter) && (!to_me) && (data[0] & 0x01))
to_me = _eth_hash_lookup(dev->hash, data); to_me = _eth_hash_lookup(dev->hash, data);
#endif /* USE_BPF */ break;
}
/* detect reception of loopback packet to our physical address */ /* detect reception of loopback packet to our physical address */
if (LOOPBACK_SELF_FRAME(dev->physical_addr, data)) { if (LOOPBACK_SELF_FRAME(dev->physical_addr, data)) {
@ -2179,20 +2319,15 @@ f
dev->loopback_self_sent--; dev->loopback_self_sent--;
to_me = 0; to_me = 0;
} }
#ifndef USE_BPF
else else
if (!bpf_used)
from_me = 0; from_me = 0;
#endif
#ifdef USE_READER_THREAD #ifdef USE_READER_THREAD
pthread_mutex_unlock (&dev->self_lock); pthread_mutex_unlock (&dev->self_lock);
#endif #endif
} }
#ifdef USE_BPF if (bpf_used ? to_me : (to_me && !from_me)) {
if (to_me) {
#else /* !USE_BPF */
if (to_me && !from_me) {
#endif
if (header->len > ETH_MIN_JUMBO_FRAME) { if (header->len > ETH_MIN_JUMBO_FRAME) {
if (header->len <= header->caplen) /* Whole Frame captured? */ if (header->len <= header->caplen) /* Whole Frame captured? */
_eth_fix_ip_jumbo_offload(dev, data, header->len); _eth_fix_ip_jumbo_offload(dev, data, header->len);
@ -2230,7 +2365,7 @@ f
pthread_mutex_unlock (&dev->lock); pthread_mutex_unlock (&dev->lock);
free(moved_data); free(moved_data);
} }
#else #else /* !USE_READER_THREAD */
/* set data in passed read packet */ /* set data in passed read packet */
dev->read_packet->len = header->len; dev->read_packet->len = header->len;
memcpy(dev->read_packet->msg, data, header->len); memcpy(dev->read_packet->msg, data, header->len);
@ -2281,10 +2416,13 @@ int eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
/* dispatch read request to either receive a filtered packet or timeout */ /* dispatch read request to either receive a filtered packet or timeout */
do { do {
if (dev->pcap_mode) switch (dev->eth_api) {
case ETH_API_PCAP:
status = pcap_dispatch((pcap_t*)dev->handle, 1, &_eth_callback, (u_char*)dev); status = pcap_dispatch((pcap_t*)dev->handle, 1, &_eth_callback, (u_char*)dev);
break;
#ifdef USE_TAP_NETWORK #ifdef USE_TAP_NETWORK
else { case ETH_API_TAP:
if (1) {
struct pcap_pkthdr header; struct pcap_pkthdr header;
int len; int len;
u_char buf[ETH_MAX_JUMBO_FRAME]; u_char buf[ETH_MAX_JUMBO_FRAME];
@ -2295,11 +2433,32 @@ int eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
status = 1; status = 1;
header.caplen = header.len = len; header.caplen = header.len = len;
_eth_callback((u_char *)dev, &header, buf); _eth_callback((u_char *)dev, &header, buf);
} else { }
else
status = 0; status = 0;
} }
} break;
#endif /* USE_TAP_NETWORK */ #endif /* USE_TAP_NETWORK */
#ifdef USE_VDE_NETWORK
case ETH_API_VDE:
if (1) {
struct pcap_pkthdr header;
int len;
u_char buf[ETH_MAX_JUMBO_FRAME];
memset(&header, 0, sizeof(header));
len = vde_recv((VDECONN*)dev->handle, buf, sizeof(buf), 0);
if (len > 0) {
status = 1;
header.caplen = header.len = len;
_eth_callback((u_char *)dev, &header, buf);
}
else
status = 0;
}
break;
#endif /* USE_VDE_NETWORK */
}
} while ((status) && (0 == packet->len)); } while ((status) && (0 == packet->len));
#else /* USE_READER_THREAD */ #else /* USE_READER_THREAD */
@ -2467,12 +2626,11 @@ t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses,
sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf); sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf);
/* get netmask, which is required for compiling */ /* get netmask, which is required for compiling */
if (dev->pcap_mode && (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0)) { if ((dev->eth_api == ETH_API_PCAP) && (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0))
bpf_netmask = 0; bpf_netmask = 0;
}
#ifdef USE_BPF #ifdef USE_BPF
if (dev->pcap_mode) { if (dev->eth_api == ETH_API_PCAP) {
/* compile filter string */ /* compile filter string */
if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) {
sprintf(errbuf, "%s", pcap_geterr(dev->handle)); sprintf(errbuf, "%s", pcap_geterr(dev->handle));
@ -2484,7 +2642,8 @@ t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses,
msg = "Eth: BPF string is: |%s|\r\n"; msg = "Eth: BPF string is: |%s|\r\n";
printf (msg, buf); printf (msg, buf);
if (sim_log) fprintf (sim_log, msg, buf); if (sim_log) fprintf (sim_log, msg, buf);
} else { }
else {
/* apply compiled filter string */ /* apply compiled filter string */
if ((status = pcap_setfilter(dev->handle, &bpf)) < 0) { if ((status = pcap_setfilter(dev->handle, &bpf)) < 0) {
sprintf(errbuf, "%s", pcap_geterr(dev->handle)); sprintf(errbuf, "%s", pcap_geterr(dev->handle));
@ -2492,7 +2651,8 @@ t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses,
printf(msg, errbuf); printf(msg, errbuf);
if (sim_log) fprintf (sim_log, msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf);
sim_debug(dev->dbit, dev->dptr, "Eth: pcap_setfilter error: %s\n", errbuf); sim_debug(dev->dbit, dev->dptr, "Eth: pcap_setfilter error: %s\n", errbuf);
} else { }
else {
#ifdef USE_SETNONBLOCK #ifdef USE_SETNONBLOCK
/* set file non-blocking */ /* set file non-blocking */
status = pcap_setnonblock (dev->handle, 1, errbuf); status = pcap_setnonblock (dev->handle, 1, errbuf);
@ -2567,9 +2727,8 @@ int eth_host_devices(int used, int max, ETH_LIST* list)
sprintf( regkey, "SYSTEM\\CurrentControlSet\\Control\\Network\\" sprintf( regkey, "SYSTEM\\CurrentControlSet\\Control\\Network\\"
"{4D36E972-E325-11CE-BFC1-08002BE10318}\\%hs\\Connection", list[i].name+ "{4D36E972-E325-11CE-BFC1-08002BE10318}\\%hs\\Connection", list[i].name+
strlen( "\\Device\\NPF_" ) ); strlen( "\\Device\\NPF_" ) );
if((status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, regkey, 0, KEY_QUERY_VALUE, &reghnd)) != ERROR_SUCCESS) { if ((status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, regkey, 0, KEY_QUERY_VALUE, &reghnd)) != ERROR_SUCCESS)
continue; continue;
}
reglen = sizeof(regval); reglen = sizeof(regval);
/* look for user-defined adapter name, bail if not found */ /* look for user-defined adapter name, bail if not found */
@ -2602,24 +2761,33 @@ int eth_host_devices(int used, int max, ETH_LIST* list)
++used; ++used;
} }
#endif #endif
#ifdef USE_VDE_NETWORK
if (used < max) {
list[used].num = used;
sprintf(list[used].name, "%s", "vde:device");
sprintf(list[used].desc, "%s", "Integrated VDE support");
++used;
}
#endif
return used; return used;
} }
int eth_devices(int max, ETH_LIST* list) int eth_devices(int max, ETH_LIST* list)
{ {
int i = 0;
#ifndef DONT_USE_PCAP_FINDALLDEVS
pcap_if_t* alldevs; pcap_if_t* alldevs;
pcap_if_t* dev; pcap_if_t* dev;
int i = 0;
char errbuf[PCAP_ERRBUF_SIZE]; char errbuf[PCAP_ERRBUF_SIZE];
#ifndef DONT_USE_PCAP_FINDALLDEVS
/* retrieve the device list */ /* retrieve the device list */
if (pcap_findalldevs(&alldevs, errbuf) == -1) { if (pcap_findalldevs(&alldevs, errbuf) == -1) {
char* msg = "Eth: error in pcap_findalldevs: %s\r\n"; char* msg = "Eth: error in pcap_findalldevs: %s\r\n";
printf (msg, errbuf); printf (msg, errbuf);
if (sim_log) fprintf (sim_log, msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf);
} else { }
else {
/* copy device list into the passed structure */ /* copy device list into the passed structure */
for (i=0, dev=alldevs; dev; dev=dev->next) { for (i=0, dev=alldevs; dev; dev=dev->next) {
if ((dev->flags & PCAP_IF_LOOPBACK) || (!strcmp("any", dev->name))) continue; if ((dev->flags & PCAP_IF_LOOPBACK) || (!strcmp("any", dev->name))) continue;

View file

@ -28,6 +28,7 @@
Modification history: Modification history:
30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking
18-Apr-11 MP Fixed race condition with self loopback packets in 18-Apr-11 MP Fixed race condition with self loopback packets in
multithreaded environments multithreaded environments
09-Dec-10 MP Added support to determine if network address conflicts exist 09-Dec-10 MP Added support to determine if network address conflicts exist
@ -89,13 +90,13 @@
#if defined (USE_READER_THREAD) #if defined (USE_READER_THREAD)
#if defined (USE_SETNONBLOCK) #if defined (USE_SETNONBLOCK)
#undef USE_SETNONBLOCK #undef USE_SETNONBLOCK
#endif #endif /* USE_SETNONBLOCK */
#undef PCAP_READ_TIMEOUT #undef PCAP_READ_TIMEOUT
#define PCAP_READ_TIMEOUT 15 #define PCAP_READ_TIMEOUT 15
#if !defined (xBSD) && !defined(_WIN32) && !defined(VMS) #if (!defined (xBSD) && !defined(_WIN32) && !defined(VMS)) || defined (USE_TAP_NETWORK) || defined (USE_VDE_NETWORK)
#define MUST_DO_SELECT #define MUST_DO_SELECT 1
#endif
#endif #endif
#endif /* USE_READER_THREAD */
/* /*
USE_BPF is defined to let this code leverage the libpcap/OS kernel provided USE_BPF is defined to let this code leverage the libpcap/OS kernel provided
@ -175,7 +176,10 @@ struct eth_device {
char* name; /* name of ethernet device */ char* name; /* name of ethernet device */
void* handle; /* handle of implementation-specific device */ void* handle; /* handle of implementation-specific device */
int fd_handle; /* fd to kernel device (where needed) */ int fd_handle; /* fd to kernel device (where needed) */
int pcap_mode; /* Flag indicating if pcap API are being used to move packets */ int eth_api; /* Designator for which API is being used to move packets */
#define ETH_API_PCAP 0 /* Pcap API in use */
#define ETH_API_TAP 1 /* tun/tap API in use */
#define ETH_API_VDE 2 /* VDE API in use */
ETH_PCALLBACK read_callback; /* read callback function */ ETH_PCALLBACK read_callback; /* read callback function */
ETH_PCALLBACK write_callback; /* write callback function */ ETH_PCALLBACK write_callback; /* write callback function */
ETH_PACK* read_packet; /* read packet */ ETH_PACK* read_packet; /* read packet */

View file

@ -320,14 +320,16 @@ return SCPE_NOFNC;
struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx;
pthread_attr_t attr; pthread_attr_t attr;
ctx->asynch_io = 1; ctx->asynch_io = sim_asynch_enabled;
ctx->asynch_io_latency = latency; ctx->asynch_io_latency = latency;
if (ctx->asynch_io) {
pthread_mutex_init (&ctx->io_lock, NULL); pthread_mutex_init (&ctx->io_lock, NULL);
pthread_cond_init (&ctx->io_cond, NULL); pthread_cond_init (&ctx->io_cond, NULL);
pthread_attr_init(&attr); pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_create (&ctx->io_thread, &attr, _tape_io, (void *)uptr); pthread_create (&ctx->io_thread, &attr, _tape_io, (void *)uptr);
pthread_attr_destroy(&attr); pthread_attr_destroy(&attr);
}
uptr->a_check_completion = _tape_completion_dispatch; uptr->a_check_completion = _tape_completion_dispatch;
#endif #endif
return SCPE_OK; return SCPE_OK;
@ -362,6 +364,7 @@ static void _sim_tape_io_flush (UNIT *uptr)
{ {
#if defined (SIM_ASYNCH_IO) #if defined (SIM_ASYNCH_IO)
sim_tape_clr_async (uptr); sim_tape_clr_async (uptr);
if (sim_asynch_enabled)
sim_tape_set_async (uptr, 0); sim_tape_set_async (uptr, 0);
#endif #endif
fflush (uptr->fileref); fflush (uptr->fileref);

View file

@ -23,6 +23,28 @@
used in advertising or otherwise to promote the sale, use or other dealings used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik. in this Software without prior written authorization from Robert M Supnik.
21-Oct-11 MP Fixed throttling in several ways:
- Sleep for the observed clock tick size while throttling
- Recompute the throttling wait once every 10 seconds
to account for varying instruction mixes during
different phases of a simulator execution or to
accommodate the presence of other load on the host
system.
- Each of the pre-existing throttling modes (Kcps,
Mcps, and %) all compute the appropriate throttling
interval dynamically. These dynamic computations
assume that 100% of the host CPU is dedicated to
the current simulator during this computation.
This assumption may not always be true and under
certain conditions may never provide a way to
correctly determine the appropriate throttling
wait. An additional throttling mode has been added
which allows the simulator operator to explicitly
state the desired throttling wait parameters.
These are specified by:
SET THROT insts/delay
where 'insts' is the number of instructions to
execute before sleeping for 'delay' milliseconds.
22-Apr-11 MP Fixed Asynch I/O support to reasonably account cycles 22-Apr-11 MP Fixed Asynch I/O support to reasonably account cycles
when an idle wait is terminated by an external event when an idle wait is terminated by an external event
05-Jan-11 MP Added Asynch I/O support 05-Jan-11 MP Added Asynch I/O support
@ -67,6 +89,7 @@ static uint32 sim_throt_ms_stop = 0;
static uint32 sim_throt_type = 0; static uint32 sim_throt_type = 0;
static uint32 sim_throt_val = 0; static uint32 sim_throt_val = 0;
static uint32 sim_throt_state = 0; static uint32 sim_throt_state = 0;
static uint32 sim_throt_sleep_time = 0;
static int32 sim_throt_wait = 0; static int32 sim_throt_wait = 0;
extern int32 sim_interval, sim_switches; extern int32 sim_interval, sim_switches;
extern FILE *sim_log; extern FILE *sim_log;
@ -152,8 +175,7 @@ return sim_os_msec () - stime;
} }
#if defined(SIM_ASYNCH_IO) #if defined(SIM_ASYNCH_IO)
#ifndef CLOCK_REALTIME #ifdef NEED_CLOCK_GETTIME
#define CLOCK_REALTIME 1
int clock_gettime(int clk_id, struct timespec *tp) int clock_gettime(int clk_id, struct timespec *tp)
{ {
uint32 secs, ns, tod[2], unixbase[2] = {0xd53e8000, 0x019db1de}; uint32 secs, ns, tod[2], unixbase[2] = {0xd53e8000, 0x019db1de};
@ -225,12 +247,13 @@ Sleep (msec);
return sim_os_msec () - stime; return sim_os_msec () - stime;
} }
#if !defined(CLOCK_REALTIME) && defined (SIM_ASYNCH_IO) #if defined(NEED_CLOCK_GETTIME)
#define CLOCK_REALTIME 1
int clock_gettime(int clk_id, struct timespec *tp) int clock_gettime(int clk_id, struct timespec *tp)
{ {
t_uint64 now, unixbase; t_uint64 now, unixbase;
if (clk_id != CLOCK_REALTIME)
return -1;
unixbase = 116444736; unixbase = 116444736;
unixbase *= 1000000000; unixbase *= 1000000000;
GetSystemTimeAsFileTime((FILETIME*)&now); GetSystemTimeAsFileTime((FILETIME*)&now);
@ -315,8 +338,7 @@ treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI;
return sim_os_msec () - stime; return sim_os_msec () - stime;
} }
#if !defined(CLOCK_REALTIME) && defined (SIM_ASYNCH_IO) #if defined(NEED_CLOCK_GETTIME)
#define CLOCK_REALTIME 1
int clock_gettime(int clk_id, struct timespec *tp) int clock_gettime(int clk_id, struct timespec *tp)
{ {
struct timeval cur; struct timeval cur;
@ -377,8 +399,7 @@ if (tim > SIM_IDLE_MAX)
return tim; return tim;
} }
#if !defined(_POSIX_SOURCE) && defined(SIM_ASYNCH_IO) #if !defined(_POSIX_SOURCE) && defined(SIM_ASYNCH_IO)
#ifndef CLOCK_REALTIME #ifdef NEED_CLOCK_GETTIME
#define CLOCK_REALTIME 1
typedef int clockid_t; typedef int clockid_t;
int clock_gettime(clockid_t clk_id, struct timespec *tp) int clock_gettime(clockid_t clk_id, struct timespec *tp)
{ {
@ -408,6 +429,21 @@ return sim_os_msec () - stime;
#endif #endif
/* diff = min - sub */
void
sim_timespec_diff (struct timespec *diff, struct timespec *min, struct timespec *sub)
{
/* move the minuend value to the difference and operate there. */
*diff = *min;
/* Borrow as needed for the nsec value */
if (sub->tv_nsec > min->tv_nsec) {
--diff->tv_sec;
diff->tv_nsec += 1000000000;
}
diff->tv_nsec -= sub->tv_nsec;
diff->tv_sec -= sub->tv_sec;
}
#if defined(SIM_ASYNCH_IO) #if defined(SIM_ASYNCH_IO)
uint32 sim_idle_ms_sleep (unsigned int msec) uint32 sim_idle_ms_sleep (unsigned int msec)
{ {
@ -628,8 +664,11 @@ return SCPE_OK;
t_stat sim_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc) t_stat sim_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc)
{ {
if (sim_idle_enab) if (sim_idle_enab) {
fprintf (st, "idle enabled, stability wait = %ds, minimum sleep resolution = %dms", sim_idle_stable, sim_idle_rate_ms); fprintf (st, "idle enabled");
if (sim_switches & SWMASK ('D'))
fprintf (st, ", stability wait = %ds, minimum sleep resolution = %dms", sim_idle_stable, sim_idle_rate_ms);
}
else fputs ("idle disabled", st); else fputs ("idle disabled", st);
return SCPE_OK; return SCPE_OK;
} }
@ -639,7 +678,7 @@ return SCPE_OK;
t_stat sim_set_throt (int32 arg, char *cptr) t_stat sim_set_throt (int32 arg, char *cptr)
{ {
char *tptr, c; char *tptr, c;
t_value val; t_value val, val2;
if (arg == 0) { if (arg == 0) {
if ((cptr != 0) && (*cptr != 0)) if ((cptr != 0) && (*cptr != 0))
@ -653,8 +692,11 @@ else {
val = strtotv (cptr, &tptr, 10); val = strtotv (cptr, &tptr, 10);
if (cptr == tptr) if (cptr == tptr)
return SCPE_ARG; return SCPE_ARG;
sim_throt_sleep_time = sim_idle_rate_ms;
c = toupper (*tptr++); c = toupper (*tptr++);
if (*tptr != 0) if (c == '/')
val2 = strtotv (tptr, &tptr, 10);
if ((*tptr != 0) || (val == 0))
return SCPE_ARG; return SCPE_ARG;
if (c == 'M') if (c == 'M')
sim_throt_type = SIM_THROT_MCYC; sim_throt_type = SIM_THROT_MCYC;
@ -662,6 +704,9 @@ else {
sim_throt_type = SIM_THROT_KCYC; sim_throt_type = SIM_THROT_KCYC;
else if ((c == '%') && (val > 0) && (val < 100)) else if ((c == '%') && (val > 0) && (val < 100))
sim_throt_type = SIM_THROT_PCT; sim_throt_type = SIM_THROT_PCT;
else if ((c == '/') && (val2 != 0)) {
sim_throt_type = SIM_THROT_SPC;
}
else return SCPE_ARG; else return SCPE_ARG;
if (sim_idle_enab) { if (sim_idle_enab) {
printf ("Idling disabled\n"); printf ("Idling disabled\n");
@ -670,6 +715,14 @@ else {
sim_clr_idle (NULL, 0, NULL, NULL); sim_clr_idle (NULL, 0, NULL, NULL);
} }
sim_throt_val = (uint32) val; sim_throt_val = (uint32) val;
if (sim_throt_type == SIM_THROT_SPC) {
if (val2 >= sim_idle_rate_ms)
sim_throt_sleep_time = (uint32) val2;
else {
sim_throt_sleep_time = (uint32) (val2 * sim_idle_rate_ms);
sim_throt_val = (uint32) (val * sim_idle_rate_ms);
}
}
} }
return SCPE_OK; return SCPE_OK;
} }
@ -693,13 +746,17 @@ else {
fprintf (st, "Throttle = %d%%\n", sim_throt_val); fprintf (st, "Throttle = %d%%\n", sim_throt_val);
break; break;
case SIM_THROT_SPC:
fprintf (st, "Throttle = %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_val);
break;
default: default:
fprintf (st, "Throttling disabled\n"); fprintf (st, "Throttling disabled\n");
break; break;
} }
if (sim_switches & SWMASK ('D')) { if (sim_switches & SWMASK ('D')) {
fprintf (st, "Wait rate = %d ms\n", sim_idle_rate_ms); fprintf (st, "minimum sleep resolution = %d ms\n", sim_idle_rate_ms);
if (sim_throt_type != 0) if (sim_throt_type != 0)
fprintf (st, "Throttle interval = %d cycles\n", sim_throt_wait); fprintf (st, "Throttle interval = %d cycles\n", sim_throt_wait);
} }
@ -712,7 +769,6 @@ void sim_throt_sched (void)
sim_throt_state = 0; sim_throt_state = 0;
if (sim_throt_type) if (sim_throt_type)
sim_activate (&sim_throt_unit, SIM_THROT_WINIT); sim_activate (&sim_throt_unit, SIM_THROT_WINIT);
return;
} }
void sim_throt_cancel (void) void sim_throt_cancel (void)
@ -722,7 +778,8 @@ sim_cancel (&sim_throt_unit);
/* Throttle service /* Throttle service
Throttle service has three distinct states Throttle service has three distinct states used while dynamically
determining a throttling interval:
0 take initial measurement 0 take initial measurement
1 take final measurement, calculate wait values 1 take final measurement, calculate wait values
@ -734,12 +791,16 @@ t_stat sim_throt_svc (UNIT *uptr)
uint32 delta_ms; uint32 delta_ms;
double a_cps, d_cps; double a_cps, d_cps;
if (sim_throt_type == SIM_THROT_SPC) { /* Non dynamic? */
sim_throt_state = 2; /* force state */
sim_throt_wait = sim_throt_val;
}
switch (sim_throt_state) { switch (sim_throt_state) {
case 0: /* take initial reading */ case 0: /* take initial reading */
sim_throt_ms_start = sim_os_msec (); sim_throt_ms_start = sim_os_msec ();
sim_throt_wait = SIM_THROT_WST; sim_throt_wait = SIM_THROT_WST;
sim_throt_state++; /* next state */ sim_throt_state = 1; /* next state */
break; /* reschedule */ break; /* reschedule */
case 1: /* take final reading */ case 1: /* take final reading */
@ -761,24 +822,32 @@ switch (sim_throt_state) {
d_cps = (double) sim_throt_val * 1000.0; d_cps = (double) sim_throt_val * 1000.0;
else d_cps = (a_cps * ((double) sim_throt_val)) / 100.0; else d_cps = (a_cps * ((double) sim_throt_val)) / 100.0;
if (d_cps >= a_cps) { if (d_cps >= a_cps) {
sim_throt_state = 0; sim_throt_sched (); /* start over */
return SCPE_OK; return SCPE_OK;
} }
sim_throt_wait = (int32) /* time between waits */ sim_throt_wait = (int32) /* time between waits */
((a_cps * d_cps * ((double) sim_idle_rate_ms)) / ((a_cps * d_cps * ((double) sim_idle_rate_ms)) /
(1000.0 * (a_cps - d_cps))); (1000.0 * (a_cps - d_cps)));
if (sim_throt_wait < SIM_THROT_WMIN) { /* not long enough? */ if (sim_throt_wait < SIM_THROT_WMIN) { /* not long enough? */
sim_throt_state = 0; sim_throt_sched (); /* start over */
return SCPE_OK; return SCPE_OK;
} }
sim_throt_state++; sim_throt_ms_start = sim_throt_ms_stop;
sim_throt_state = 2;
// fprintf (stderr, "Throttle values a_cps = %f, d_cps = %f, wait = %d\n", // fprintf (stderr, "Throttle values a_cps = %f, d_cps = %f, wait = %d\n",
// a_cps, d_cps, sim_throt_wait); // a_cps, d_cps, sim_throt_wait);
} }
break; break;
case 2: /* throttling */ case 2: /* throttling */
sim_os_ms_sleep (1); sim_os_ms_sleep (sim_throt_sleep_time);
delta_ms = sim_os_msec () - sim_throt_ms_start;
if ((sim_throt_type != SIM_THROT_SPC) && /* when dynamic throttling */
(delta_ms >= 10000)) { /* recompute every 10 sec */
sim_throt_ms_start = sim_os_msec ();
sim_throt_wait = SIM_THROT_WST;
sim_throt_state = 1; /* next state */
}
break; break;
} }

View file

@ -31,6 +31,26 @@
#ifndef _SIM_TIMER_H_ #ifndef _SIM_TIMER_H_
#define _SIM_TIMER_H_ 0 #define _SIM_TIMER_H_ 0
#include <time.h>
#if defined (__APPLE__)
#define HAVE_STRUCT_TIMESPEC 1 /* OSX defined the structure but doesn't tell us */
#endif
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME 1
#define NEED_CLOCK_GETTIME 1
#ifndef HAVE_STRUCT_TIMESPEC
#define HAVE_STRUCT_TIMESPEC 1
struct timespec {
long tv_sec;
long tv_nsec;
};
#endif /* HAVE_STRUCT_TIMESPEC */
int clock_gettime(int clock_id, struct timespec *tp);
#endif
#define SIM_NTIMERS 8 /* # timers */ #define SIM_NTIMERS 8 /* # timers */
#define SIM_TMAX 500 /* max timer makeup */ #define SIM_TMAX 500 /* max timer makeup */
@ -46,11 +66,13 @@
#define SIM_THROT_WMIN 100 /* min wait */ #define SIM_THROT_WMIN 100 /* min wait */
#define SIM_THROT_MSMIN 10 /* min for measurement */ #define SIM_THROT_MSMIN 10 /* min for measurement */
#define SIM_THROT_NONE 0 /* throttle parameters */ #define SIM_THROT_NONE 0 /* throttle parameters */
#define SIM_THROT_MCYC 1 #define SIM_THROT_MCYC 1 /* MegaCycles Per Sec */
#define SIM_THROT_KCYC 2 #define SIM_THROT_KCYC 2 /* KiloCycles Per Sec */
#define SIM_THROT_PCT 3 #define SIM_THROT_PCT 3 /* Max Percent of host CPU */
#define SIM_THROT_SPC 4 /* Specific periodic Delay */
t_bool sim_timer_init (void); t_bool sim_timer_init (void);
void sim_timespec_diff (struct timespec *diff, struct timespec *min, struct timespec *sub);
int32 sim_rtcn_init (int32 time, int32 tmr); int32 sim_rtcn_init (int32 time, int32 tmr);
void sim_rtcn_init_all (void); void sim_rtcn_init_all (void);
int32 sim_rtcn_calb (int32 ticksper, int32 tmr); int32 sim_rtcn_calb (int32 ticksper, int32 tmr);