From a9fd3dd51891464beba588be64b4284338ab2ef1 Mon Sep 17 00:00:00 2001 From: Bob Supnik Date: Wed, 23 Mar 2011 14:39:00 -0700 Subject: [PATCH] Notes For V3.8 The makefile now works for Linux and most Unix's. However, for Solaris and MacOS, you must first export the OSTYPE environment variable: > export OSTYPE > make Otherwise, you will get build errors. 1. New Features 1.1 3.8-0 1.1.1 SCP and Libraries - BREAK, NOBREAK, and SHOW BREAK with no argument will set, clear, and show (respectively) a breakpoint at the current PC. 1.1.2 GRI - Added support for the GRI-99 processor. 1.1.3 HP2100 - Added support for the BACI terminal interface. - Added support for RTE OS/VMA/EMA, SIGNAL, VIS firmware extensions. 1.1.4 Nova - Added support for 64KW memory (implemented in third-party CPU's). 1.1.5 PDP-11 - Added support for DC11, RC11, KE11A, KG11A. - Added modem control support for DL11. - Added ASCII character support for all 8b devices. 1.2 3.8-1 1.2.1 SCP and libraries - Added capability to set line connection order for terminal multiplexers. 1.2.2 HP2100 - Added support for 12620A/12936A privileged interrupt fence. - Added support for 12792C eight-channel asynchronous multiplexer. 1.3 3.8-2 1.3.1 SCP and libraries - Added line history capability for *nix hosts. - Added "SHOW SHOW" and "SHOW SHOW" commands. 1.3.2 1401 - Added "no rewind" option to magtape boot. 1.3.3 PDP-11 - Added RD32 support to RQ - Added debug support to RL 1.3.4 PDP-8 - Added FPP support (many thanks to Rick Murphy for debugging the code) 1.3.5 VAX-11/780 - Added AUTORESTART switch support, and VMS REBOOT command support 2. Bugs Fixed Please see the revision history on http://simh.trailing-edge.com or in the source module sim_rev.h. --- 0readme_38.txt | 15 +- 0readme_ethernet.txt | 387 ++++-- AltairZ80/altairZ80_sio.c | 91 +- GRI/gri_defs.h | 12 +- H316/h316_defs.h | 7 +- H316/h316_mt.c | 3 +- HP2100/hp2100_ms.c | 9 +- I1401/i1401_cpu.c | 20 +- I1401/i1401_defs.h | 11 +- I1401/i1401_mt.c | 83 +- I1620/i1620_defs.h | 7 +- I7094/i7094_bug_history.txt | 14 +- I7094/i7094_com.c | 523 ++++---- I7094/i7094_com_old.c | 1179 +++++++++++++++++ I7094/i7094_cpu.c | 70 +- I7094/i7094_cpu1.c | 57 +- I7094/i7094_cpu1_old.c | 887 +++++++++++++ I7094/i7094_cpu_old.c | 2439 +++++++++++++++++++++++++++++++++++ I7094/i7094_defs.h | 14 +- I7094/i7094_mt.c | 26 +- I7094/i7094_mt_old.c | 861 +++++++++++++ I7094/i7094_sys.c | 47 +- I7094/i7094_sys_old.c | 748 +++++++++++ Interdata/id_defs.h | 7 +- LGP/lgp_defs.h | 9 +- NOVA/nova_defs.h | 7 +- PDP1/pdp1_defs.h | 7 +- PDP10/pdp10_defs.h | 7 +- PDP11/pdp11_defs.h | 7 +- PDP11/pdp11_tq.c | 127 +- PDP11/pdp11_ts.c | 22 +- PDP11/pdp11_xq.c | 1032 +++++++++++++-- PDP11/pdp11_xq.h | 187 ++- PDP11/pdp11_xu.c | 180 ++- PDP11/pdp11_xu.h | 9 +- PDP18B/pdp18b_defs.h | 7 +- PDP8/pdp8_clk.c | 2 +- PDP8/pdp8_cpu.c | 8 +- PDP8/pdp8_ct.c | 4 +- PDP8/pdp8_defs.h | 7 +- PDP8/pdp8_df.c | 6 +- PDP8/pdp8_dt.c | 4 +- PDP8/pdp8_fpp.c | 2 +- PDP8/pdp8_lp.c | 2 +- PDP8/pdp8_mt.c | 2 +- PDP8/pdp8_pt.c | 2 +- PDP8/pdp8_rf.c | 6 +- PDP8/pdp8_rk.c | 2 +- PDP8/pdp8_rl.c | 4 +- PDP8/pdp8_rx.c | 6 +- PDP8/pdp8_sys.c | 4 +- PDP8/pdp8_td.c | 2 +- PDP8/pdp8_tsc.c | 2 +- PDP8/pdp8_tt.c | 2 +- PDP8/pdp8_ttx.c | 2 +- SDS/sds_defs.h | 7 +- SDS/sds_mt.c | 2 +- VAX/vax780_defs.h | 16 +- VAX/vax780_sbi.c | 41 +- VAX/vax780_stddev.c | 11 +- VAX/vax780_uba.c | 2 +- VAX/vax_cpu.c | 106 +- VAX/vax_cpu1.c | 12 +- VAX/vax_sys.c | 5 +- VAX/vax_syscm.c | 12 +- VAX/vax_sysdev.c | 5 +- makefile | 46 +- scp.c | 177 ++- sim_console.c | 69 +- sim_console_old.c | 1180 +++++++++++++++++ sim_ether.c | 1810 +++++++++++++++++++++----- sim_ether.h | 77 +- sim_rev.h | 508 ++++---- sim_sock.c | 11 +- sim_timer.c | 22 +- sim_tmxr.c | 64 +- sim_tmxr_old.c | 998 ++++++++++++++ 77 files changed, 12846 insertions(+), 1522 deletions(-) create mode 100644 I7094/i7094_com_old.c create mode 100644 I7094/i7094_cpu1_old.c create mode 100644 I7094/i7094_cpu_old.c create mode 100644 I7094/i7094_mt_old.c create mode 100644 I7094/i7094_sys_old.c create mode 100644 sim_console_old.c create mode 100644 sim_tmxr_old.c diff --git a/0readme_38.txt b/0readme_38.txt index befc49c0..5ed20e65 100644 --- a/0readme_38.txt +++ b/0readme_38.txt @@ -51,19 +51,28 @@ Otherwise, you will get build errors. 1.3 3.8-2 -1.3.1 1401 +1.3.1 SCP and libraries + +- Added line history capability for *nix hosts. +- Added "SHOW SHOW" and "SHOW SHOW" commands. + +1.3.2 1401 - Added "no rewind" option to magtape boot. -1.3.2 PDP-11 +1.3.3 PDP-11 - Added RD32 support to RQ - Added debug support to RL -1.3.3 PDP-8 +1.3.4 PDP-8 - Added FPP support (many thanks to Rick Murphy for debugging the code) +1.3.5 VAX-11/780 + +- Added AUTORESTART switch support, and VMS REBOOT command support + 2. Bugs Fixed diff --git a/0readme_ethernet.txt b/0readme_ethernet.txt index 4cc74fb3..9c8c7401 100644 --- a/0readme_ethernet.txt +++ b/0readme_ethernet.txt @@ -2,45 +2,125 @@ This file contains information about the SIMH Ethernet package. ------------------------------------------------------------------------------- -The XQ emulator is a host-independant software emulation of Digital's -DELQA (M7516) and DEQNA (M7504) Q-bus ethernet cards for the SIMH emulator. +The XQ emulator is a host-independent software emulation of Digital's +DELQA-T (M7516-YM), DELQA (M7516) and DEQNA (M7504) Q-bus Ethernet cards +for the SIMH emulator. -The XU emulator is a host-independant software emulation of Digital's DEUNA -(M7792/M7793) and DELUA (M7521) Unibus ethernet cards for the SIMH emulator. +The XU emulator is a host-independent software emulation of Digital's DEUNA +(M7792/M7793) and DELUA (M7521) Unibus Ethernet cards for the SIMH emulator. The XQ and XU simulators use the Sim_Ether module to execute host-specific -packet reads and writes, since all operating systems talk to real ethernet +packet reads and writes, since all operating systems talk to real Ethernet cards/controllers differently. See the comments at the top of sim_ether.c for the list of currently supported host platforms. -The Sim_Ether module sets the selected ethernet card into +The Sim_Ether module sets the selected Ethernet card into promiscuous mode to gather all packets, then filters out the packets that it doesn't want. In Windows, packets having the same source MAC address as the controller are ignored for WinPCAP compatibility (see Windows notes below). -If your ethernet card is plugged into a switch, the promiscuous mode setting +If your Ethernet card is plugged into a switch, the promiscuous mode setting should not cause much of a problem, since the switch will still filter out most of the undesirable traffic. You will only see "excessive" traffic if you are on a direct or hub(repeater) segment. -Using the libpcap/WinPcap interface, the simulated computer cannot "talk" to -the host computer via the selected interface, since the packets are not -reflected back to the host. The workaround for this is to use a second NIC in -the host and connect them both into the same network; then the host and the -simulator can communicate over the physical LAN. +On Windows using the WinPcap interface, the simulated computer can "talk" to +the host computer on the same interface. On other platforms with libpcap +(*nix), the simulated computer can not "talk" to the host computer via the +selected interface, since simulator transmitted packets are not received +by the host's network stack. The workaround for this is to use a second NIC +in the host and connect them both into the same network; then the host and +the simulator can communicate over the physical LAN. -Universal TUN/TAP support provides another solution for the above dual-NIC -problem for systems that support Universal TUN/TAP. Since the TUN/TAP interface -is at a different network level, the host can create a TAP device for the -simulator and then bridge or route packets between the TAP device and the real -network interface. Note that the TAP device and any bridging or routing must be -established before running the simulator; SIMH does not create, bridge, or -route TAP devices for you. +Integrated Universal TUN/TAP support provides another solution for the above +dual-NIC problem for systems that support Universal TUN/TAP. Since the TUN/TAP +interface is a pseudo network interface, the host can create a TAP device for +the simulator and then bridge or route packets between the TAP device and the +real network interface. Note that the TAP device and any bridging or routing +must be established before running the simulator; SIMH does not create, +bridge, or route TAP devices for you. + +Integrated Universal TUN/TAP support can be used for host<->simulator network +traffic (on the platforms where it is available) by using the SIMH command: +"attach xq tap:tapN" (i.e. attach xq tap:tap0). Platforms that this has been +tested on include: Linux, FreeBSD, OpenBSD, NetBSD. Each of these platforms +has some way to create a tap pseudo device and then bridge it with a physical +network interface. + +The following steps were performed to get a working SIMH vax simulator +sharing a physical NIC and allowing Host<->SIMH vax communications: + +Linux (Ubuntu 10.04): + apt-get install bridge-utils + apt-get install uml-utilities + + + #!/bin/sh + HOSTIP=`/sbin/ifconfig eth0 | grep "inet addr" | gawk -- '{ print $2 }' | gawk -F : -- '{ print $2 }'` + HOSTNETMASK=`/sbin/ifconfig eth0 | grep "inet addr" | gawk -- '{ print $4 }' | gawk -F : -- '{ print $2 }'` + HOSTBCASTADDR=`/sbin/ifconfig eth0 | grep "inet addr" | gawk -- '{ print $3 }' | gawk -F : -- '{ print $2 }'` + HOSTDEFAULTGATEWAY=`/sbin/route -n | grep ^0.0.0.0 | gawk -- '{ print $2 }'` + # + /usr/sbin/tunctl -t tap0 [-u someuser] + /sbin/ifconfig tap0 up + # + # Now convert eth0 to a bridge and bridge it with the TAP interface + /usr/sbin/brctl addbr br0 + /usr/sbin/brctl addif br0 eth0 + /usr/sbin/brctl setfd br0 0 + /sbin/ifconfig eth0 0.0.0.0 + /sbin/ifconfig br0 $HOSTIP netmask $HOSTNETMASK broadcast $HOSTBCASTADDR up + # set the default route to the br0 interface + /sbin/route add -net 0.0.0.0/0 gw $HOSTDEFAULTGATEWAY + # bridge in the tap device + /usr/sbin/brctl addif br0 tap0 + /sbin/ifconfig tap0 0.0.0.0 + + # Run simulator and "attach xq tap:tap0" + +OpenBSD (OpenBSD 4.6) + + /sbin/ifconfig tun0 create + /sbin/ifconfig tun0 link0 + /sbin/ifconfig tun0 up + + /sbin/ifconfig bridge0 create + /sbin/brconfig bridge0 fwddelay 4 + /sbin/brconfig bridge0 add em0 add tun0 # Change em0 to reflect your physical NIC name + /sbin/brconfig bridge0 up + + # Run simulator and "attach xq tap:tun0" + +FreeBSD (FreeBSD 8.0) + + /sbin/ifconfig tap0 create + /sbin/ifconfig tap0 up + + /sbin/ifconfig bridge0 create + /sbin/ifconfig bridge0 addm em0 addm tap0 # Change em0 to reflect your physical NIC name + /sbin/ifconfig bridge0 up + + # Run simulator and "attach xq tap:tap0" + # Note: it seems that on FreeBSD you may have to + # "/sbin/ifconfig tap0 up" and "/sbin/ifconfig bridge0 up" prior to each + # time simh "attach"es the tap:tap0 device + +NetBSD (NetBSD 5.0.2) + + /sbin/ifconfig tap0 create + /sbin/ifconfig tap0 up + + /sbin/ifconfig bridge0 create + /sbin/brconfig bridge0 fwddelay 1 + /sbin/brconfig bridge0 add wm0 add tap0 # Change wm0 to reflect your physical NIC name + /sbin/brconfig bridge0 up + + # Run simulator and "attach xq tap:tap0" ------------------------------------------------------------------------------- Windows notes: - 1. The Windows-specific code uses the WinPCAP 3.0 package from + 1. The Windows-specific code uses the WinPCAP 4.x package from http://www.winpcap.org. This package for windows simulates the libpcap package that is freely available for un*x systems. @@ -48,29 +128,27 @@ Windows notes: 3. The first time the WinPCAP driver is used, it will be dynamically loaded, and the user must be an Administrator on the machine to do so. If you need - to run as an unprivileged user, you must set the service to autostart. See - the WinPCAP documentation for details on the static loading workaround. - - 4. If you want to use TAP devices, they must be created before running SIMH. - (TAP component from the OpenVPN project; http://openvpn.sourceforge.net) - - 5. Compaq PATHWORKS 32 v7.2 also enabled bridging for the ethernet adapters - when the DECNET and LAT drivers were installed; TAP was not needed. + to run as an unprivileged user, you must set the "npf" driver to autostart. + Current WinPcap installers provide an option to configure this at + installation time. Building on Windows: - 1. Install WinPCAP 3.0 runtime and the WinPCAP Developer's kit. + 1. Install WinPCAP 4.x runtime and the WinPCAP Developer's kit. - 2. Put the required .h files (bittypes,devioctl,ip6_misc,packet32,pcap, - pcap-stdinc).h from the WinPCAP 3.0 developer's kit in the compiler's path + 2. Put the required .h files (bittypes,devioctl,ip6_misc,pcap,pcap-stdinc + packet32,ntddndis).h from the WinPCAP 4.x developer's kit in the + compiler's include file path - 3. Put the required .lib files (packet,wpcap).lib from the WinPCAP 3.0 - developer's kit in the linker's path + 3. Put the required .lib files (packet,wpcap).lib from the WinPCAP 4.x + developer's kit in the linker's library path 4. If you're using Borland C++, use COFF2OMF to convert the .lib files into a format that can be used by the compiler. - 5. Define USE_NETWORK. + 5. Define USE_NETWORK. The current windows network built binaries will + run on any system. regardless of whether or not WinPcap is installed, + and will provide Network functionality when WinPcap is available. 6. Build it! @@ -83,30 +161,33 @@ Linux, {Free|Net|Open}BSD, OS/X, and Un*x notes: Sim_Ether has been reworked to be more universal; because of this, you will need to get a version of libpcap that is 0.9 or greater. This can be downloaded from www.tcpdump.org - see the comments at the top of Sim_ether.c -for details. - -At the time of this release, the "Current Version" available at: -http://www.tcpdump.org/daily/libpcap-current.tar.gz is the -latest checked-in source code that is actually higher than the released -0.8.3 version number. Specifically, for all platforms, it contains code that -opens the ethernet device in Read/Write mode instead of the Read-Only mode -that previous libpcap versions for platforms which use one of pcap-bpf.c, -pcap-pf.c, or pcap-snit.c. This capabiligy now exists to support a newly -provided generic packet sending capability. +for details. ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- 1. For all platforms, you must run SIMH(scp) with sufficient privilege to - allow the ethernet card can be set into promiscuous mode and to write - packets through the driver. For most Unix/Unix-like platforms this will - mean running as root. For systems which use bpf devices (NetBSD, - OpenBSD, FreeBSD and OS/X) it is possible to set permissions on the bpf - devices to allow read and write access to users other than root (For - example: chmod 666 /dev/bpf*). Doing this, has its own security issues. + allow the Ethernet card can be set into promiscuous mode and to write + packets through the driver. + a) For Windows systems this means having administrator privileges to + start the "npf" driver. The current WinPcap installer offers an + option to autostart the "npf" driver when the system boots. + b) For more recent Linux systems, The concepts leveraging "Filesystem + Capabilities" can be used to specifically grant the simh binary + the needed privileges to access the network. The article at: + http://packetlife.net/blog/2010/mar/19/sniffing-wireshark-non-root-user/ + describes how to do this for wireshark. The exact same capabilities + are needed by SIMH for network support. Use that article as a guide. + c) For Unix/Unix-like systems which use bpf devices (NetBSD, + OpenBSD, FreeBSD and OS/X) it is possible to set permissions on + the bpf devices to allow read and write access to users other + than root (For example: chmod 666 /dev/bpf*). Doing this, has + its own security issues. + d) For other platforms this will likely mean running as root. Additional alternative methods for avoiding the 'run as root' requirement will be welcomed. - 2. If you want to use TAP devices, they must be created before running SIMH. + 2. If you want to use TAP devices, and any surrounding system network/bridge + setup must be done before running SIMH. Building on Linux, {Free|Net|Open}BSD, OS/X, Un*x: @@ -116,12 +197,18 @@ Building on Linux, {Free|Net|Open}BSD, OS/X, Un*x: Linux : search for your variant on http://rpmfind.net OS/X : Apple Developer's site? - NOTE: These repositories will not likely contain a version - of libpcap greater than 0.8.1 for several years since - other packages in these repositories don't depend on a - later version than they currently have. + NOTE: These repositories for older versions of these platforms + don't contain a version of libpcap greater than 0.8.1. + However, most(all) recent releases of *nix environments ship + with sufficiently recent versions of libpcap either automatically + installed or available for installation as part of the + distribution. - 2. Use 'make USE_NETWORK=1' + 2. If you install the vendor supplied libpcap-dev package and it provides + a /usr/lib/libpcap.a file, then the existing makefile will automatically + use the vendor supplied library without any additional arguments. + If you have downloaded and built the libpcap from tcpdump.org, then + you can use it during a build by typing 'make USE_NETWORK=1' 3. Build it! @@ -132,27 +219,27 @@ OpenVMS Alpha notes: when required VCI promiscuous mode support was added. Hobbyists can get the required version of VMS from the OpenVMS Alpha Hobbyist Kit 3.0. - Running a simulator built with ethernet support on a version of VMS prior - to 7.3-1 will behave as if there is no ethernet support built in due to + Running a simulator built with Ethernet support on a version of VMS prior + to 7.3-1 will behave as if there is no Ethernet support built in due to the inability of the software to set the PCAPVCM into promiscuous mode. - An example display of fully functional ethernet support: + An example display of fully functional Ethernet support: sim> SHOW XQ ETH ETH devices: 0 we0 (VMS Device: _EWA0:) 1 we1 (VMS Device: _EWB0:) - An example display when the simulator was built without ethernet support + An example display when the simulator was built without Ethernet support or is not running the required version of VMS: sim> SHOW XQ ETH ETH devices: no network devices are available 2. You must place the PCAPVCM.EXE execlet in SYS$LOADABLE_IMAGES before - running a simulator with ethernet support. Note: This is done by the + running a simulator with Ethernet support. Note: This is done by the build commands in descrip.mms. - 3. You must have CMKRNL privilege to SHOW or ATTACH an ethernet device; + 3. You must have CMKRNL privilege to SHOW or ATTACH an Ethernet device; alternatively, you can INSTALL the simulator with CMKRNL privilege. 4. If you use a second adapter to communicate to the host, SOME protocol @@ -162,13 +249,13 @@ OpenVMS Alpha notes: Building on OpenVMS Alpha: The current descrip.mms file will build simulators capable of using - ethernet support with them automatically. These currently are: VAX, + Ethernet support with them automatically. These currently are: VAX, PDP11, and PDP10. The descrip.mms driven builds will also build the pcap library and build and install the VCI execlet. 1. Fetch the VMS-PCAP zip file from: http://simh.trailing-edge.com/sources/vms-pcap.zip - 2. Unzip it into the base of the simh distribution directory. + 2. Unzip it into the base of the SIMH distribution directory. 3. Build the simulator(s) with MMS or MMK: $ MMx {VAX,PDP11,PDP10, etc...} @@ -195,15 +282,15 @@ RSX11M+ system image that also contains DECNET, LAT, and/or TCP/IP software. ------------------------------------------------------------------------------- -How to debug problems with the ethernet subsystems: +How to debug problems with the Ethernet subsystems: PLEASE read the host-specific notes in sim_ether.c! While running SCP, the following commands can be used to enable debug messages: - scp> SET DEBUG STDERR - scp> SET XQ DEBUG={ETH|TRC|REG|WRN|CSR|VAR|SAN|SET|PCK} - scp> SET XU DEBUG={ETH|TRC|REG|WRN} + sim> SET DEBUG STDERR + sim> SET XQ DEBUG=TRACE;CSR;VAR;WARN;SETUP;SANITY;REG;PACKET;DATA;ETH + sim> SET XU DEBUG=ETH;TRACE;REG;WARN;PACKET;DATA Documentation of the functionality of these debug modifiers can be found in pdp11_xq.h and pdp11_xu.h. Inline debugging has replaced the previous #ifdef @@ -212,16 +299,13 @@ style of debugging, which required recompilation before debugging. ------------------------------------------------------------------------------- Things planned for future releases: - 1. PDP-11 bootstrap/bootrom - 2. Full MOP implementation - 3. DESQA support (if someone can get me the user manuals) - 4. DETQA support [DELQA-Turbo] (I have the manual) + 1. Full MOP implementation ------------------------------------------------------------------------------- Things which I need help with: 1. Information about Remote MOP processing - 2. VAX/PDP-11 hardware diagnotics image files and docs, to test XQ thoroughly. + 2. VAX/PDP-11 hardware diagnostics image files and docs, to test XQ thoroughly. 3. Feedback on operation with other VAX/PDP-11 OS's. ------------------------------------------------------------------------------- @@ -237,11 +321,160 @@ Dave Change Log =============================================================================== + 12-Jan-11 DTH Added SHOW XU FILTERS modifier + 11-Jan-11 DTH Corrected DEUNA/DELUA SELFTEST command, enabling use by + VMS 3.7, VMS 4.7, and Ultrix 1.1 + 09-Jan-11 MP Fixed missing crc data when USE_READER_THREAD is defined and + crc's are needed (only the pdp11_xu) + 16-Dec-10 MP added priority boost for read and write threads when + USE_READER_THREAD does I/O in separate threads. This helps + throughput since it allows these I/O bound threads to preempt + the main thread (which is executing simulated instructions). + 09-Dec-10 MP allowed more flexible parsing of MAC address strings + 09-Dec-10 MP Added support to determine if network address conflicts exist + 07-Dec-10 MP Reworked DECnet self detection to the more general approach + of loopback self when any Physical Address is being set. + 06-Dec-10 MP Added loopback processing support to pdp11_xu.c + 06-Dec-10 MP Fixed loopback processing to correctly handle forward packets. + 04-Dec-10 MP Changed eth_write to do nonblocking writes when + USE_READER_THREAD is defined. + 30-Nov-10 MP Fixed the fact that no broadcast packets were received by the DEUNA + 29-Nov-10 MP Fixed interrupt dispatch issue which caused delivered packets + (in and out) to sometimes not interrupt the CPU after processing. + 17-Jun-10 MP Fixed bug in the AUTODIN II hash filtering. + 14-Jun-10 MP Added support for integrated Tap networking interfaces on BSD + platforms. + 13-Jun-10 MP Added support for integrated Tap networking interfaces on Linux + platforms. + 31-May-10 MP Added support for more TOE (TCP Offload Engine) features for IPv4 + network traffic from the host and/or from hosts on the LAN. These + new TOE features are: LSO (Large Send Offload) and Jumbo packet + fragmentation support. These features allow a simulated network + device to support traffic when a host leverages a NIC's Large + Send Offload capabilities to fragment and/or segment outgoing + network traffic. Additionally a simulated network device can + reasonably exist on a LAN which is configured to use Jumbo frames. + 21-May-10 MP Added functionality to fix up IP header checksums to accommodate + packets from a host with a NIC which has TOE (TCP Offload Engine) + enabled which is expected to implement the checksum computations + in hardware. Since we catch packets before they arrive at the + NIC the expected checksum insertions haven't been performed yet. + This processing is only done for packets sent from the host to + the guest we're supporting. In general this will be a relatively + small number of packets so it is done for all IP frame packets + coming from the host to the guest. In order to make the + determination of packets specifically arriving from the host we + need to know the hardware MAC address of the host NIC. Currently + determining a NIC's MAC address is relatively easy on Windows. + The non-windows code works on linux and may work on other *nix + platforms either as is or with slight modifications. The code, + as implemented, only messes with this activity if the host + interface MAC address can be determined. + 20-May-10 MP Added general support to deal with receiving packets smaller + than ETH_MIN_PACKET in length. These come from packets + looped back by some bridging mechanism and need to be padded + to the minimum frame size. A real NIC won't pass us any + packets like that. This fix belongs here since this layer + is responsible for interfacing to the physical layer + devices, AND it belongs here to get CRC processing right. + 15-Aug-08 MP Fixed transmitted packets to have the correct source MAC address. + Fixed incorrect address filter setting calling eth_filter(). + 07-Mar-08 MP Fixed the SCP visible SA registers to always display the + ROM MAC address, even after it is changed by SET XQ MAC=. + 07-Mar-08 MP Added changes so that the Console DELQA diagnostic (>>>TEST 82) + will succeed. + 03-Mar-08 MP Added DELQA-T (aka DELQA Plus) device emulation support. + 06-Feb-08 MP Added dropped frame statistics to record when the receiver discards + received packets due to the receiver being disabled, or due to the + XQ device's packet receive queue being full. + Fixed bug in receive processing when we're not polling. This could + cause receive processing to never be activated again if we don't + read all available packets via eth_read each time we get the + opportunity. + 31-Jan-08 MP Added the ability to Coalesce received packet interrupts. This + is enabled by SET XQ POLL=DELAY=nnn where nnn is a number of + microseconds to delay the triggering of an interrupt when a packet + is received. + 29-Jan-08 MP Added SET XQ POLL=DISABLE (aka SET XQ POLL=0) to operate without + polling for packet read completion. + 29-Jan-08 MP Changed the sanity and id timer mechanisms to use a separate timer + unit so that transmit and receive activities can be dealt with + by the normal xq_svc routine. + Dynamically determine the timer polling rate based on the + calibrated tmr_poll and clk_tps values of the simulator. + 25-Jan-08 MP Enabled the SET XQ POLL to be meaningful if the simulator currently + doesn't support idling. + 25-Jan-08 MP Changed xq_debug_setup to use sim_debug instead of printf so that + all debug output goes to the same place. + 25-Jan-08 MP Restored the call to xq_svc after all successful calls to eth_write + to allow receive processing to happen before the next event + service time. This must have been inadvertently commented out + while other things were being tested. + 23-Jan-08 MP Added debugging support to display packet headers and packet data + 18-Jun-07 RMS Added UNIT_IDLE flag + 29-Oct-06 RMS Synced poll and clock + 27-Jan-06 RMS Fixed unaligned accesses in XQB (found by Doug Carman) + 07-Jan-06 RMS Fixed unaligned access bugs (found by Doug Carman) + 07-Sep-05 DTH Removed unused variable + 16-Aug-05 RMS Fixed C++ declaration and cast problems + + 05-Mar-08 MP Added optional multicast filtering support for doing + LANCE style AUTODIN II based hashed filtering. + 07-Feb-08 MP Added eth_show_dev to display Ethernet state + Changed the return value from eth_read to return whether + or not a packet was read. No existing callers used or + checked constant return value that previously was being + supplied. + 29-Jan-08 MP Added eth_set_async to provide a mechanism (when + USE_READER_THREAD is enabled) to allow packet reception + to dynamically update the simulator event queue and + potentially avoid polling for I/O. This provides a minimal + overhead (no polling) maximal responsiveness for network + activities. + 29-Jan-08 MP Properly sequenced activities in eth_close to avoid a race + condition when USE_READER_THREAD is enabled. + 25-Jan-08 MP Changed the following when USE_READER_THREAD is enabled: + - Fixed bug when the simulated device doesn't need crc + in packet data which is read. + - Added call to pcap_setmintocopy to minimize packet + delivery latencies. + - Added ethq_destroy and used it to avoid a memory leak in + eth_close. + - Properly cleaned up pthread mutexes in eth_close. + Migrated to using sim_os_ms_sleep for a delay instead of + a call to select(). + Fixed the bpf filter used when no traffic is to be matched. + Reworked eth_add_packet_crc32 implementation to avoid an + extra buffer copy while reading packets. + Fixed up #ifdef's relating to USE_SHARED so that setting + USE_SHARED or USE_NETWORK will build a working network + environment. + 23-Jan-08 MP Reworked eth_packet_trace and eth_packet_trace_ex to allow + only output Ethernet header+crc and provide a mechanism for + the simulated device to display full packet data debugging. + 17-May-07 DTH Fixed non-Ethernet device removal loop (from Naoki Hamada) + 15-May-07 DTH Added dynamic loading of wpcap.dll; + Corrected exceed max index bug in ethX lookup + 04-May-07 DTH Corrected failure to look up Ethernet device names in + the registry on Windows XP x64 + 10-Jul-06 RMS Fixed linux conditionalization (from Chaskiel Grundman) + 02-Jun-06 JDB Fixed compiler warning for incompatible sscanf parameter + 15-Dec-05 DTH Patched eth_host_devices [remove non-Ethernet devices] + (from Mark Pizzolato and Galen Tackett, 08-Jun-05) + Patched eth_open [tun fix](from Antal Ritter, 06-Oct-05) + 30-Nov-05 DTH Added option to regenerate CRC on received packets; some + Ethernet devices need to pass it on to the simulation, and by + the time libpcap/winpcap gets the packet, the host OS network + layer has already stripped CRC out of the packet + 01-Dec-04 DTH Added Windows user-defined adapter names (from Timothe Litt) + + + 19-Mar-04 Release: 1. Genericized Sim_Ether code, reduced #ifdefs (David Hittner) 2. Further refinement of sim_ether, qualified more platforms (Mark Pizzolato) 3. Added XU module (David Hittner) - 4. Corrected XQ interrupt signalling for PDP11s (David Hittner) + 4. Corrected XQ interrupt signaling for PDP11s (David Hittner) 5. Added inline debugging support (David Hittner) ------------------------------------------------------------------------------- @@ -251,7 +484,7 @@ Dave 2. Added DECNET duplicate detection for Windows (Mark Pizzolato) 3. Added BPF filtering to increase efficiency (Mark Pizzolato) 4. Corrected XQ Runt processing (Mark Pizzolato) - 5. Corrected XQ Sofware Reset (Mark Pizzolato) + 5. Corrected XQ Software Reset (Mark Pizzolato) 6. Corrected XQ Multicast/Promiscuous mode setting/resetting (Mark Pizzolato) 7. Added Universal TUN/TAP support (Mark Pizzolato) 8. Added FreeBSD support (Edward Brocklesby) @@ -271,7 +504,7 @@ Dave 1. Corrected bug in xq_setmac introduced in v3.0 (multiple people) 2. Made XQ rcv buffer allocation dynamic to reduce scp size (David Hittner) 3. Optimized some structs, removed legacy variables (Mark Pizzolato) - 4. Changed #ifdef WIN32 to _WIN32 for consistancy (Mark Pizzolato) + 4. Changed #ifdef WIN32 to _WIN32 for consistency (Mark Pizzolato) ------------------------------------------------------------------------------- @@ -308,7 +541,7 @@ Dave 08-Nov-02 Release: 1. Added USE_NETWORK conditional to Sim_Ether - 2. Fixed behaviour of SHOW XQ ETH if no devices exist + 2. Fixed behavior of SHOW XQ ETH if no devices exist 3. Added OpenBSD support to Sim_Ether (courtesy of Federico Schwindt) 4. Added ethX detection simplification (from Megan Gentry) diff --git a/AltairZ80/altairZ80_sio.c b/AltairZ80/altairZ80_sio.c index 1ae1b935..dfa0a9bb 100644 --- a/AltairZ80/altairZ80_sio.c +++ b/AltairZ80/altairZ80_sio.c @@ -63,10 +63,11 @@ #endif /* Debug flags */ -#define IN_MSG (1 << 0) -#define OUT_MSG (1 << 1) -#define CMD_MSG (1 << 2) -#define VERBOSE_MSG (1 << 3) +#define IN_MSG (1 << 0) +#define OUT_MSG (1 << 1) +#define CMD_MSG (1 << 2) +#define VERBOSE_MSG (1 << 3) +#define BUFFER_EMPTY_MSG (1 << 4) #define UNIT_V_SIO_ANSI (UNIT_V_UF + 0) /* ANSI mode, strip bit 8 on output */ #define UNIT_SIO_ANSI (1 << UNIT_V_SIO_ANSI) @@ -160,11 +161,12 @@ extern int32 sim_interval; /* Debug Flags */ static DEBTAB generic_dt[] = { - { "IN", IN_MSG }, - { "OUT", OUT_MSG }, - { "CMD", CMD_MSG }, - { "VERBOSE", VERBOSE_MSG }, - { NULL, 0 } + { "IN", IN_MSG }, + { "OUT", OUT_MSG }, + { "CMD", CMD_MSG }, + { "VERBOSE", VERBOSE_MSG }, + { "BUFFEREMPTY", BUFFER_EMPTY_MSG }, + { NULL, 0 } }; /* SIMH pseudo device status registers */ @@ -256,7 +258,7 @@ static UNIT sio_unit = { 100000, /* wait */ FALSE, /* u3 = FALSE, no character available in buffer */ FALSE, /* u4 = FALSE, terminal input is not attached to a file */ - FALSE, /* u5 = FALSE, terminal input has not yet reached EOF */ + 0, /* u5 = 0, not used */ 0 /* u6 = 0, not used */ }; @@ -267,7 +269,6 @@ static REG sio_reg[] = { { DRDATA (WRNPTRE, warnPTREOF, 32) }, { DRDATA (WRUPORT, warnUnassignedPort, 32) }, { HRDATA (FILEATT, sio_unit.u4, 8), REG_RO }, /* TRUE iff terminal input is attached to a file */ - { HRDATA (FILEEOF, sio_unit.u5, 8), REG_RO }, /* TRUE iff terminal input file has reached EOF */ { HRDATA (TSTATUS, sio_unit.u3, 8) }, /* TRUE iff a character available in sio_unit.buf */ { DRDATA (TBUFFER, sio_unit.buf, 8) }, /* input buffer for one character */ { DRDATA (KEYBDI, keyboardInterrupt, 3), REG_RO }, @@ -412,7 +413,6 @@ static t_stat sio_attach(UNIT *uptr, char *cptr) { return tmxr_attach(&altairTMXR, uptr, cptr); /* attach mux */ } sio_unit.u4 = TRUE; /* terminal input is attached to a file */ - sio_unit.u5 = FALSE; /* EOF not yet reached */ return attach_unit(uptr, cptr); } @@ -440,11 +440,10 @@ static t_stat sio_reset(DEVICE *dptr) { int32 i; TRACE_PRINT(sio_dev, VERBOSE_MSG, ("SIO: " ADDRESS_FORMAT " Reset" NLP, PCX)); sio_unit.u3 = FALSE; /* no character in terminal input buffer */ + sio_unit.buf = 0; resetSIOWarningFlags(); - if (sio_unit.u4) { /* is terminal input attached to a file? */ + if (sio_unit.u4) /* is terminal input attached to a file? */ rewind(sio_unit.fileref); /* yes, rewind input */ - sio_unit.u5 = FALSE; /* EOF not yet reached */ - } else if (sio_unit.flags & UNIT_ATT) for (i = 0; i < TERMINALS; i++) if (TerminalLines[i].conn) @@ -457,6 +456,7 @@ static t_stat ptr_reset(DEVICE *dptr) { TRACE_PRINT(ptr_dev, VERBOSE_MSG, ("PTR: " ADDRESS_FORMAT " Reset" NLP, PCX)); resetSIOWarningFlags(); ptr_unit.u3 = FALSE; /* End Of File not yet reached */ + ptr_unit.buf = 0; if (ptr_unit.flags & UNIT_ATT) /* attached? */ rewind(ptr_unit.fileref); sim_map_resource(0x12, 1, RESOURCE_TYPE_IO, &sio1s, dptr->flags & DEV_DIS); @@ -629,15 +629,23 @@ static void voidSleep(void) { /* generic status port for keyboard input / terminal output */ static int32 sio0sCore(const int32 port, const int32 io, const int32 data) { int32 ch, result; - SIO_PORT_INFO spi = lookupPortInfo(port, &ch); + const SIO_PORT_INFO spi = lookupPortInfo(port, &ch); assert(spi.port == port); pollConnection(); if (io == 0) { /* IN */ if (sio_unit.u4) { /* attached to a file? */ - if (sio_unit.u5) /* EOF reached? */ - sio_detach(&sio_unit); /* detach file and switch to keyboard input */ - else + if (sio_unit.u3) /* character available? */ return spi.sio_can_read | spi.sio_can_write; + ch = getc(sio_unit.fileref); + if (ch == EOF) { + sio_detach(&sio_unit); /* detach file and switch to keyboard input */ + return spi.sio_cannot_read | spi.sio_can_write; + } + else { + sio_unit.u3 = TRUE; /* indicate character available */ + sio_unit.buf = ch; /* store character in buffer */ + return spi.sio_can_read | spi.sio_can_write; + } } if (sio_unit.flags & UNIT_ATT) { /* attached to a port? */ if (tmxr_rqln(&TerminalLines[spi.terminalLine])) @@ -658,19 +666,20 @@ static int32 sio0sCore(const int32 port, const int32 io, const int32 data) { if (ch == SCPE_STOP) { /* stop CPU in case ^E (default) was typed */ stop_cpu = TRUE; sim_interval = 0; /* detect stop condition as soon as possible*/ - return spi.sio_can_write | spi.sio_cannot_read; /* do not consume stop character */ + return spi.sio_cannot_read | spi.sio_can_write; /* do not consume stop character */ } sio_unit.u3 = TRUE; /* indicate character available */ sio_unit.buf = ch; /* store character in buffer */ return spi.sio_can_read | spi.sio_can_write; } checkSleep(); - return spi.sio_can_write | spi.sio_cannot_read; + return spi.sio_cannot_read | spi.sio_can_write; } /* OUT follows, no fall-through from IN */ if (spi.hasReset && (data == spi.sio_reset)) { /* reset command */ - sio_unit.u3 = FALSE; /* indicate that no character is available */ + if (!sio_unit.u4) /* only reset for regular console I/O */ + sio_unit.u3 = FALSE; /* indicate that no character is available */ TRACE_PRINT(sio_dev, CMD_MSG, - ("SIO: " ADDRESS_FORMAT " Command OUT(0x%03x) = 0x%02x" NLP, PCX, port, data)); + ("\tSIO_S: " ADDRESS_FORMAT " Command OUT(0x%03x) = 0x%02x" NLP, PCX, port, data)); } return 0x00; /* ignored since OUT */ } @@ -678,32 +687,23 @@ static int32 sio0sCore(const int32 port, const int32 io, const int32 data) { int32 sio0s(const int32 port, const int32 io, const int32 data) { const int32 result = sio0sCore(port, io, data); if ((io == 0) && (sio_dev.dctrl & IN_MSG)) - printf("SIO_S: " ADDRESS_FORMAT " IN(0x%03x) = 0x%02x" NLP, PCX, port, result); + printf("\tSIO_S: " ADDRESS_FORMAT " IN(0x%03x) = 0x%02x" NLP, PCX, port, result); else if ((io) && (sio_dev.dctrl & OUT_MSG)) - printf("SIO_S: " ADDRESS_FORMAT " OUT(0x%03x) = 0x%02x" NLP, PCX, port, data); + printf("\tSIO_S: " ADDRESS_FORMAT " OUT(0x%03x) = 0x%02x" NLP, PCX, port, data); return result; } /* generic data port for keyboard input / terminal output */ static int32 sio0dCore(const int32 port, const int32 io, const int32 data) { int32 ch; - SIO_PORT_INFO spi = lookupPortInfo(port, &ch); + const SIO_PORT_INFO spi = lookupPortInfo(port, &ch); assert(spi.port == port); pollConnection(); if (io == 0) { /* IN */ - if (sio_unit.u4) { /* attached to a file? */ - if (sio_unit.u5) { /* EOF reached? */ - sio_detach(&sio_unit); /* detach file and switch to keyboard input */ - return CONTROLC_CHAR; /* this time return ^C after all */ - } - if ((ch = getc(sio_unit.fileref)) == EOF) { /* end of file? */ - sio_unit.u5 = TRUE; /* terminal input file has reached EOF */ - return CONTROLC_CHAR; /* result is ^C (= CP/M interrupt) */ - } - return mapCharacter(ch); /* return mapped character */ - } - if (sio_unit.flags & UNIT_ATT) + if ((sio_unit.flags & UNIT_ATT) && (!sio_unit.u4)) return mapCharacter(tmxr_getc_ln(&TerminalLines[spi.terminalLine])); + if ((!sio_unit.u3) && (sio_dev.dctrl & BUFFER_EMPTY_MSG)) + printf("\tSIO_D: " ADDRESS_FORMAT " IN(0x%03x) for empty character buffer" NLP, PCX, port); sio_unit.u3 = FALSE; /* no character is available any more */ return mapCharacter(sio_unit.buf); /* return previous character */ } /* OUT follows, no fall-through from IN */ @@ -720,12 +720,21 @@ static int32 sio0dCore(const int32 port, const int32 io, const int32 data) { return 0x00; /* ignored since OUT */ } +static char* printable(char* result, int32 data, const int32 isIn) { + result[0] = 0; + data &= 0x7f; + if ((0x20 <= data) && (data < 0x7f)) + sprintf(result, isIn ? " <-\"%c\"" : " ->\"%c\"", data); + return result; +} + int32 sio0d(const int32 port, const int32 io, const int32 data) { + char buffer[8]; const int32 result = sio0dCore(port, io, data); if ((io == 0) && (sio_dev.dctrl & IN_MSG)) - printf("SIO_D: " ADDRESS_FORMAT " IN(0x%03x) = 0x%02x" NLP, PCX, port, result); - else if ((io) && (sio_dev.dctrl & OUT_MSG)) - printf("SIO_D: " ADDRESS_FORMAT " OUT(0x%03x) = 0x%02x" NLP, PCX, port, data); + printf("\tSIO_D: " ADDRESS_FORMAT " IN(0x%03x) = 0x%02x%s" NLP, PCX, port, result, printable(buffer, result, TRUE)); + else if ((io) && (sio_dev.dctrl & OUT_MSG)) + printf("\tSIO_D: " ADDRESS_FORMAT " OUT(0x%03x) = 0x%02x%s" NLP, PCX, port, data, printable(buffer, data, FALSE)); return result; } diff --git a/GRI/gri_defs.h b/GRI/gri_defs.h index 35eff8f4..034d3337 100644 --- a/GRI/gri_defs.h +++ b/GRI/gri_defs.h @@ -1,6 +1,6 @@ /* gri_defs.h: GRI-909 simulator definitions - Copyright (c) 2001-2008, Robert M. Supnik + Copyright (c) 2001-2010, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 22-May-10 RMS Added check for 64b definitions 12-Jan-08 RMS Added GRI-99 support 25-Apr-03 RMS Revised for extended file support 19-Sep-02 RMS Fixed declarations in gdev structure @@ -46,8 +47,15 @@ 5. How does the EAO handle divide overflow? Answer: set link. */ +#ifndef _GRI_DEFS_H_ +#define _GRI_DEFS_H_ 0 + #include "sim_defs.h" /* simulator defns */ +#if defined(USE_INT64) || defined(USE_ADDR64) +#error "GRI does not support 64b values!" +#endif + /* Simulator stop codes */ #define STOP_DEV 1 /* must be 1 */ @@ -244,3 +252,5 @@ struct gdev { #define VEC_CASR 0047 #define VEC_DISK 0055 /* disk */ #define VEC_RTC 0100 /* clock */ + +#endif diff --git a/H316/h316_defs.h b/H316/h316_defs.h index 409342d1..00952096 100644 --- a/H316/h316_defs.h +++ b/H316/h316_defs.h @@ -1,6 +1,6 @@ /* h316_defs.h: Honeywell 316/516 simulator definitions - Copyright (c) 1999-2008, Robert M. Supnik + Copyright (c) 1999-2010, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 22-May-10 RMS Added check for 64b definitions 15-Feb-05 RMS Added start button interrupt 01-Dec-04 RMS Added double precision constants 24-Oct-03 RMS Added DMA/DMC support @@ -34,6 +35,10 @@ #include "sim_defs.h" /* simulator defns */ +#if defined(USE_INT64) || defined(USE_ADDR64) +#error "H316 does not support 64b values!" +#endif + /* Simulator stop codes */ #define STOP_RSRV 1 /* must be 1 */ diff --git a/H316/h316_mt.c b/H316/h316_mt.c index 6ec7e6df..5ca516f4 100644 --- a/H316/h316_mt.c +++ b/H316/h316_mt.c @@ -274,7 +274,8 @@ switch (inst) { /* case on opcode */ break; case 003: /* !BOT */ - if (!(uptr->UST & STA_BOT)) return IOSKIP (dat); + if (!(uptr->UST & STA_BOT)) + return IOSKIP (dat); break; case 004: /* !interrupting */ diff --git a/HP2100/hp2100_ms.c b/HP2100/hp2100_ms.c index 4d2e64fc..fc0490b7 100644 --- a/HP2100/hp2100_ms.c +++ b/HP2100/hp2100_ms.c @@ -732,14 +732,16 @@ switch (uptr->FNC) { /* case on function */ case FNC_FSR: /* space forward */ if (st = sim_tape_sprecf (uptr, &tbc)) /* space rec fwd, err? */ r = ms_map_err (uptr, st); /* map error */ - if (tbc & 1) msc_sta = msc_sta | STA_ODD; + if (tbc & 1) + msc_sta = msc_sta | STA_ODD; else msc_sta = msc_sta & ~STA_ODD; break; case FNC_BSR: /* space reverse */ if (st = sim_tape_sprecr (uptr, &tbc)) /* space rec rev, err? */ r = ms_map_err (uptr, st); /* map error */ - if (tbc & 1) msc_sta = msc_sta | STA_ODD; + if (tbc & 1) + msc_sta = msc_sta | STA_ODD; else msc_sta = msc_sta & ~STA_ODD; break; @@ -786,7 +788,8 @@ switch (uptr->FNC) { /* case on function */ sim_activate (uptr, msc_xtime); /* re-activate */ return SCPE_OK; } - if (ms_max & 1) msc_sta = msc_sta | STA_ODD; /* set ODD by rec len */ + if (ms_max & 1) /* set ODD by rec len */ + msc_sta = msc_sta | STA_ODD; else msc_sta = msc_sta & ~STA_ODD; sim_activate (uptr, msc_itime); /* sched IRG */ if (uptr->FNC == FNC_RFF) msc_1st = 1; /* diagnostic? */ diff --git a/I1401/i1401_cpu.c b/I1401/i1401_cpu.c index 3a1196a0..b07d272f 100644 --- a/I1401/i1401_cpu.c +++ b/I1401/i1401_cpu.c @@ -1,6 +1,6 @@ /* i1401_cpu.c: IBM 1401 CPU simulator - Copyright (c) 1993-2010, Robert M. Supnik + Copyright (c) 1993-2011, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,9 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 19-Mar-11 RMS Reverted multiple tape indicator implementation + 20-Jan-11 RMS Fixed branch on EOT indicator per hardware (from Van Snyder) + 07-Nov-10 RMS Fixed divide not to clear word marks in quotient 24-Apr-10 RMS Revised divide algorithm (from Van Snyder) 11-Jul-08 RMS Added missing A magtape modifier (from Van Snyder) Fixed tape indicator implementation (from Bob Abeles) @@ -226,7 +229,6 @@ extern t_stat inq_io (int32 flag, int32 mod); extern t_stat mt_io (int32 unit, int32 flag, int32 mod); extern t_stat dp_io (int32 fnc, int32 flag, int32 mod); extern t_stat mt_func (int32 unit, int32 flag, int32 mod); -extern t_bool mt_testind (void); extern t_stat sim_activate (UNIT *uptr, int32 delay); extern t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw); @@ -432,7 +434,7 @@ static const int32 ind_table[64] = { 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 17 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */ 0, 1, 1, 0, 1, 0, 0, 0, /* 30 - 37 */ - 0, 0, 1, 0, 0, 0, 0, 0, /* 40 - 47 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 47 */ 0, 0, 1, 0, 1, 0, 0, 0, /* 50 - 57 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 67 */ 0, 0, 1, 0, 0, 0, 0, 0 /* 70 - 77 */ @@ -841,12 +843,6 @@ CHECK_LENGTH: BRANCH; } else if (ilnt == 5) { /* branch on ind? */ - if (D == IN_END) { /* tape indicator */ - if (mt_testind ()) { /* test, reset */ - BRANCH; - } - else break; - } if (ind[D]) { /* test indicator */ BRANCH; } @@ -1706,7 +1702,7 @@ do { r = bcd_to_bin[b & DIGIT] + /* sum digits + c */ bcd_to_bin[a & DIGIT] + c; c = (r >= 10); /* set carry out */ - M[bp] = sum_table[r]; /* store result */ + M[bp] = (M[bp] & WM) | sum_table[r]; /* store result */ ap--; bp--; } while ((a & WM) == 0); @@ -1726,14 +1722,14 @@ do { r = bcd_to_bin[b & DIGIT] - /* a - b - borrow */ bcd_to_bin[a & DIGIT] - c; c = (r < 0); /* set borrow out */ - M[bp] = sum_table[r + 10]; /* store result */ + M[bp] = (M[bp] & WM) | sum_table[r + 10]; /* store result */ ap--; bp--; } while ((a & WM) == 0); b = M[bp]; /* borrow position */ if (bcd_to_bin[b & DIGIT] != 0) { /* non-zero? */ r = bcd_to_bin[b & DIGIT] - c; /* subtract borrow */ - M[bp] = sum_table[r]; /* store result */ + M[bp] = (M[bp] & WM) | sum_table[r]; /* store result */ return 0; /* subtract worked */ } return c; /* return borrow */ diff --git a/I1401/i1401_defs.h b/I1401/i1401_defs.h index 373cec7b..65affa25 100644 --- a/I1401/i1401_defs.h +++ b/I1401/i1401_defs.h @@ -1,6 +1,6 @@ /* i1401_defs.h: IBM 1401 simulator definitions - Copyright (c) 1993-2008, Robert M. Supnik + Copyright (c) 1993-2010, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,8 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 06-JUl-10 RMS Added overlap indicator definitions + 22-May-10 RMS Added check for 64b definitions 11-Jul-08 RMS Added IO mode flag for boot (from Bob Abeles) 28-Jun-07 RMS Defined character code for tape mark 14-Nov-04 RMS Added column binary support @@ -42,6 +44,10 @@ #include "sim_defs.h" +#if defined(USE_INT64) || defined(USE_ADDR64) +#error "1401 does not support 64b values!" +#endif + /* Simulator stop codes */ #define STOP_NXI 1 /* unimpl instr */ @@ -267,6 +273,7 @@ #define IN_LPT 032 /* printer error */ #define IN_PRO 034 /* process check */ #define IN_DBY 036 /* disk busy */ +#define IN_TBY 041 /* tape busy */ #define IN_END 042 /* end indicator */ #define IN_TAP 043 /* tape error */ #define IN_ACC 045 /* access error */ @@ -282,6 +289,8 @@ #define IN_SSE 065 /* sense switch E */ #define IN_SSF 066 /* sense switch F */ #define IN_SSG 067 /* sense switch G */ +#define IN_RBY 070 /* reader busy */ +#define IN_PBY 071 /* punch busy */ #define IN_READ 072 /* reader error */ #define CRETIOE(f,c) return ((f)? (c): SCPE_OK) diff --git a/I1401/i1401_mt.c b/I1401/i1401_mt.c index 54c97ef1..56b3cba0 100644 --- a/I1401/i1401_mt.c +++ b/I1401/i1401_mt.c @@ -1,6 +1,6 @@ /* i1401_mt.c: IBM 1401 magnetic tape simulator - Copyright (c) 1993-2010, Robert M. Supnik + Copyright (c) 1993-2011, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,10 @@ mt 7-track magtape + 19-Mar-11 RMS Restored lost edit to insert EOF in memory on read EOF + Reverted multiple tape indicator implementation + 20-Jan-11 RMS Fixed branch on END indicator per hardware (from Van Snyder) + 26-Jun-10 RMS Fixed backspace over tapemark not to set EOR (from Van Snyder) 11-Jul-08 RMS Added -n (no rewind) option to BOOT (from Van Snyder) Added tape mark detect to diagnostic read (from Bob Abeles) Added tape mark detect in multi-character records (from Bob Abeles) @@ -102,10 +106,8 @@ #define MT_NUMDR 7 /* #drives */ #define MT_MAXFR (MAXMEMSIZE * 2) /* max transfer */ -#define IND u3 /* drive indicator */ uint8 dbuf[MT_MAXFR]; /* tape buffer */ -int32 mt_sel = 0; /* selected unit */ extern uint8 M[]; /* memory */ extern int32 ind[64]; @@ -115,8 +117,6 @@ extern FILE *sim_deb; t_stat mt_reset (DEVICE *dptr); t_stat mt_boot (int32 unitno, DEVICE *dptr); -t_stat mt_attach (UNIT *uptr, char *cp); -t_stat mt_detach (UNIT *uptr); t_stat mt_map_status (t_stat st); UNIT *mt_sel_unit (int32 unit); @@ -145,13 +145,8 @@ UNIT mt_unit[] = { }; REG mt_reg[] = { + { FLDATA (IND, ind[IN_END], 0) }, { FLDATA (ERR, ind[IN_TAP], 0) }, - { FLDATA (IND1, mt_unit[1].IND, 0) }, - { FLDATA (IND2, mt_unit[2].IND, 0) }, - { FLDATA (IND3, mt_unit[3].IND, 0) }, - { FLDATA (IND4, mt_unit[4].IND, 0) }, - { FLDATA (IND5, mt_unit[5].IND, 0) }, - { FLDATA (IND6, mt_unit[6].IND, 0) }, { DRDATA (POS1, mt_unit[1].pos, T_ADDR_W), PV_LEFT + REG_RO }, { DRDATA (POS2, mt_unit[2].pos, T_ADDR_W), PV_LEFT + REG_RO }, { DRDATA (POS3, mt_unit[3].pos, T_ADDR_W), PV_LEFT + REG_RO }, @@ -175,7 +170,7 @@ DEVICE mt_dev = { "MT", mt_unit, mt_reg, mt_mod, MT_NUMDR, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, - &mt_boot, &mt_attach, &mt_detach, + &mt_boot, NULL, NULL, NULL, DEV_DEBUG }; @@ -195,11 +190,12 @@ t_mtrlnt tbc; UNIT *uptr; t_stat st; -ind[IN_TAP] = 0; /* clear error */ if ((uptr = mt_sel_unit (unit)) == NULL) /* sel unit, save */ return STOP_INVMTU; /* (not valid) */ if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_UNATT; +ind[IN_TAP] = 0; /* clear error */ +ind[IN_END] = 0; /* clear indicator */ switch (mod) { /* case on modifier */ case BCD_A: /* diagnostic read */ @@ -215,8 +211,8 @@ switch (mod) { /* case on modifier */ break; } if (!(flag & MD_BIN) && /* BCD tape and */ - ((dbuf[0] & CHAR) == BCD_TAPMRK)) /* first char TMK? */ - uptr->IND = 1; /* set indicator */ + ((dbuf[0] & CHAR) == BCD_TAPMRK)) /* first char TMK? */ + ind[IN_END] = 1; /* set indicator */ } break; @@ -224,6 +220,8 @@ switch (mod) { /* case on modifier */ if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, ">>MT%d: backspace\n", unit); st = sim_tape_sprecr (uptr, &tbc); /* space rev */ + if (st == MTSE_TMK) /* ignore TMK */ + st = MTSE_OK; break; /* end case */ case BCD_E: /* erase = nop */ @@ -249,8 +247,7 @@ switch (mod) { /* case on modifier */ if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, ">>MT%d: rewind and unload\n", unit); sim_tape_rewind (uptr); /* update position */ - st = mt_detach (uptr); /* detach */ - break; + return sim_tape_detach (uptr); /* detach */ default: return STOP_INVM; @@ -283,11 +280,12 @@ t_stat st; t_bool passed_eot; UNIT *uptr; -ind[IN_TAP] = 0; /* clear error */ if ((uptr = mt_sel_unit (unit)) == NULL) /* sel unit, save */ return STOP_INVMTU; /* (not valid) */ if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_UNATT; +ind[IN_TAP] = 0; /* clear error */ +ind[IN_END] = 0; /* clear indicator */ switch (mod) { @@ -296,7 +294,12 @@ switch (mod) { fprintf (sim_deb, ">>MT%d: read from %d", unit, BS); wm_seen = 0; /* no word mk seen */ st = sim_tape_rdrecf (uptr, dbuf, &tbc, MT_MAXFR); /* read rec */ - if (st != MTSE_TMK) { /* not tmk? */ + if (st == MTSE_TMK) { /* tape mark? */ + ind[IN_END] = 1; /* set indicator */ + tbc = 1; /* one char read */ + dbuf[0] = BCD_TAPMRK; /* BCD tapemark */ + } + else { /* not tmk? */ if (st == MTSE_RECE) /* rec in error? */ ind[IN_TAP] = 1; else if (st != MTSE_OK) { /* stop on error */ @@ -306,7 +309,7 @@ switch (mod) { } if (!(flag & MD_BIN) && /* BCD tape and */ ((dbuf[0] & CHAR) == BCD_TAPMRK)) /* first char TMK? */ - uptr->IND = 1; /* set indicator */ + ind[IN_END] = 1; /* set indicator */ } for (i = 0; i < tbc; i++) { /* loop thru buf */ if (!(flag & MD_BOOT) && /* not boot? check */ @@ -390,13 +393,12 @@ switch (mod) { return mt_map_status (st); } -/* Select unit - save selection if valid, return unit pointer */ +/* Select unit - return unit pointer if valid */ UNIT *mt_sel_unit (int32 unit) { if ((unit <= 0) || (unit >= MT_NUMDR)) return NULL; -mt_sel = unit; /* save selected unit */ return mt_dev.units + unit; } @@ -420,8 +422,7 @@ switch (st) { return SCPE_MTRLNT; case MTSE_TMK: /* end of file */ - if (mt_sel != 0) - mt_unit[mt_sel].IND = 1; /* set indicator latch */ + ind[IN_END] = 1; /* set indicator */ break; case MTSE_IOERR: /* IO error */ @@ -442,21 +443,6 @@ switch (st) { return SCPE_OK; } -/* Test and reset selected tape drive's indicator latch */ - -t_bool mt_testind () -{ -t_bool v; - -if (mt_sel == 0) /* any mt selected? */ - return FALSE; -if (!(mt_unit[mt_sel].flags & UNIT_ATT)) /* attached? */ - return FALSE; -v = mt_unit[mt_sel].IND; /* save latch */ -mt_unit[mt_sel].IND = 0; /* reset latch */ -return v; -} - /* Reset routine */ t_stat mt_reset (DEVICE *dptr) @@ -467,11 +453,10 @@ UNIT *uptr; for (i = 0; i < MT_NUMDR; i++) { /* per drive resets */ if (uptr = mt_sel_unit (i)) { MT_CLR_PNU (uptr); /* clear pos flag */ - uptr->IND = 0; /* clear ind latch */ } } ind[IN_TAP] = 0; /* clear mt err ind */ -mt_sel = 0; /* clear unit select */ +ind[IN_END] = 0; /* clear mt end ind */ return SCPE_OK; } @@ -493,19 +478,3 @@ mt_io (unitno, MD_WM + MD_BOOT, BCD_R); /* LDA %U1 001 R */ saved_IS = 1; return SCPE_OK; } - -/* Attach routine */ - -t_stat mt_attach (UNIT *uptr, char *cp) -{ -uptr->IND = 0; /* reset indicator latch */ -return sim_tape_attach (uptr, cp); /* tape unit attach */ -} - -/* Detach routine */ - -t_stat mt_detach (UNIT *uptr) -{ -uptr->IND = 0; /* reset indicator latch */ -return sim_tape_detach (uptr); /* tape unit detach */ -} diff --git a/I1620/i1620_defs.h b/I1620/i1620_defs.h index ec671606..d4183c78 100644 --- a/I1620/i1620_defs.h +++ b/I1620/i1620_defs.h @@ -1,6 +1,6 @@ /* i1620_defs.h: IBM 1620 simulator definitions - Copyright (c) 2002-2008, Robert M. Supnik + Copyright (c) 2002-2010, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -27,6 +27,7 @@ I am grateful to Al Kossow, the Computer History Museum, and the IBM Corporate Archives for their help in gathering documentation about the IBM 1620. + 22-May-10 RMS Added check for 64b definitions 18-Oct-02 RMS Fixed bug in ADDR_S macro (found by Hans Pufal) */ @@ -35,6 +36,10 @@ #include "sim_defs.h" /* simulator defns */ +#if defined(USE_INT64) || defined(USE_ADDR64) +#error "1620 does not support 64b values!" +#endif + /* Simulator stop codes */ #define STOP_HALT 1 /* HALT */ diff --git a/I7094/i7094_bug_history.txt b/I7094/i7094_bug_history.txt index e8fb15b8..9b1c5cf8 100644 --- a/I7094/i7094_bug_history.txt +++ b/I7094/i7094_bug_history.txt @@ -20,8 +20,8 @@ Bugs Found and Fixed During Simulator Debug 18. CPU: Shift count is 8b wide, not 9b. 19. SYS: Mnemonic is LDQ not LMQ. 20. SYS: RQL opcode incorrect in symbolic decode table. -21. CPU: Multi-tag mode stores OR'd value of tags on any index read except - normal effective address. +21. CPU: Multi-tag mode stores OR'd value of tags on any index read except normal + effective address. 22. CPU: Floating point trap does not write location 0 if trap suppressed. 23. SYS: TRA instructions should have symbolic class MXN not MXR. 24. CPU: Floating add instructions test for zero result only if normalization enabled. @@ -58,12 +58,10 @@ Bugs Found and Fixed During Simulator Debug 63. IO: 7607 channel modeled incorrectly, could stall. 64. IO: All 7607 "effective NOP" conditions must be tested when a new command is decoded (wc == 0 for IOCx and IOSx, EOR set for IOSx and IORx). - - - - - - +65. MT: BSR, BSF release the data channel in a few microseconds, like REW. +66. CPU: Not all PSE and MSE variants are trapped in user mode. +67. CPU: Storage nullification mask not recalculated at all points needed; + replaced with macro. diff --git a/I7094/i7094_com.c b/I7094/i7094_com.c index 4a665453..2fd5cf24 100644 --- a/I7094/i7094_com.c +++ b/I7094/i7094_com.c @@ -1,6 +1,6 @@ /* i7094_com.c: IBM 7094 7750 communications interface simulator - Copyright (c) 2005-2008, Robert M Supnik + Copyright (c) 2005-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,6 +26,7 @@ com 7750 controller coml 7750 lines + 12-Aug-2010 RMS Major rewrite for CTSS (from Dave Pitts) 19-Nov-2008 RMS Revised for common TMXR show routines This module implements an abstract simulator for the IBM 7750 communications @@ -35,8 +36,9 @@ supports only terminals. The 7750 can handle many different kinds of terminals; the simulator supports only a limited subset. - Input is asynchronous. The 7750 sets ATN1 to signal availability of input. - When the 7094 issues a CTLRN, the 7750 gathers available input characters + Input is asynchronous and line buffered. When valid input (a line or a + control message) is available, the 7750 sets ATN1 to signal availability of + input. When the 7094 issues a CTLRN, the 7750 gathers available input characters into a message. The message has a 12b sequence number, followed by 12b line number/character pairs, followed by end-of-medium (03777). Input characters can either be control characters (bit 02000 set) or data characters. Data @@ -65,7 +67,7 @@ #include "sim_tmxr.h" #include -#define COM_MLINES 32 /* mux lines */ +#define COM_MLINES 31 /* mux lines */ #define COM_TLINES (COM_MLINES + 1) /* total lines */ #define COM_BUFSIZ 120 /* max chan transfer */ #define COM_PKTSIZ 16384 /* character buffer */ @@ -77,6 +79,8 @@ #define CONN u3 /* line is connected */ #define NEEDID u4 /* need to send ID */ +#define NOECHO u5 /* no echo */ +#define INPP u6 /* input pending */ #define COM_INIT_POLL 8000 /* polling interval */ #define COMC_WAIT 2 /* channel delay time */ @@ -113,7 +117,7 @@ #define COMO_LIN12B 0200000000000 /* line is 12b */ #define COMO_LINCTL 0100000000000 /* control msg */ #define COMO_GETLN(x) (((uint32) ((x) >> 24)) & 0777) -#define COMO_CTLRST 0000077770000 /* control reset */ +#define COMO_CTLRST 00000 /* control reset */ #define COMO_BITRPT 03777 /* bit repeat */ #define COMO_EOM12B 07777 /* end of medium */ #define COMO_BMAX 94 /* buffer max, words */ @@ -157,13 +161,9 @@ typedef struct { /* The 7750 character buffer is maintained as linked lists. The lists are: free free list - inpq input queue + inpq[ln] input queue for line n outq[ln] output queue for line ln - The input queue has two entries for each character; the first is the - line number, the second the character. The output queues have only - one entry for each character. - Links are done as subscripts in array com_pkt. This allows the list headers and the queues themselves to be saved and restored. */ @@ -172,19 +172,19 @@ uint32 com_enab = 0; /* 7750 enabled */ uint32 com_msgn = 0; /* next input msg num */ uint32 com_sta = 0; /* 7750 state */ uint32 com_stop = 0; /* channel stop */ -uint32 com_quit = 0; /* quit code */ +uint32 com_quit = 003; /* quit code */ uint32 com_intr = 0; /* interrupt code */ uint32 com_bptr = 0; /* buffer pointer */ uint32 com_blim = 0; /* buffer count */ uint32 com_tps = 50; /* polls/second */ -uint32 com_not_ret[COM_TLINES] = { 0 }; /* chars not returned */ t_uint64 com_sns = 0; /* sense word */ t_uint64 com_chob = 0; /* chan output buf */ uint32 com_chob_v = 0; /* valid flag */ t_uint64 com_buf[COM_BUFSIZ]; /* channel buffer */ LISTHD com_free; /* free list */ -LISTHD com_inpq; /* input queue */ -LISTHD com_outq[COM_TLINES]; /* output queue */ +uint32 com_not_ret[COM_TLINES] = { 0 }; /* chars not returned */ +LISTHD com_inpq[COM_TLINES] = { 0 }; /* input queues */ +LISTHD com_outq[COM_TLINES] = { 0 }; /* output queues */ LISTENT com_pkt[COM_PKTSIZ]; /* character packets */ TMLN com_ldsc[COM_MLINES] = { 0 }; /* line descriptors */ TMXR com_desc = { COM_MLINES, 0, 0, com_ldsc }; /* mux descriptor */ @@ -225,23 +225,24 @@ t_stat com_attach (UNIT *uptr, char *cptr); t_stat com_detach (UNIT *uptr); t_stat com_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat com_show_freeq (FILE *st, UNIT *uptr, int32 val, void *desc); -t_stat com_show_inq (FILE *st, UNIT *uptr, int32 val, void *desc); -t_stat com_show_aoutq (FILE *st, UNIT *uptr, int32 val, void *desc); -t_stat com_show_outq (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat com_show_allq (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat com_show_oneq (FILE *st, UNIT *uptr, int32 val, void *desc); void com_reset_ln (uint32 i); +uint16 com_get_nexti (uint32 *ln); uint16 com_gethd_free (LISTHD *lh); uint16 com_gethd (LISTHD *lh); +uint16 com_gettl_free (LISTHD *lh); +uint16 com_gettl (LISTHD *lh); t_bool com_new_puttl (LISTHD *lh, uint16 val); void com_puttl (LISTHD *lh, uint16 ent); -t_bool com_inp_msg (uint32 ln, uint16 msg); -void com_skip_outc (uint32 ln); -t_stat com_test_atn (uint32 ch); +t_bool com_test_inp (void); +void com_set_inpp (uint32 ln); t_uint64 com_getob (uint32 ch); t_bool com_qdone (uint32 ch); void com_end (uint32 ch, uint32 fl, uint32 st); t_stat com_send_id (uint32 ln); -t_stat com_send_ccmp (uint32 ln); -t_stat com_queue_in (uint32 ln, uint32 ch); +uint32 com_gen_ccmp (uint32 ln); +t_bool com_queue_in (uint32 ln, uint32 ch); uint32 com_queue_out (uint32 ln, uint32 *c1); void com_set_sns (t_uint64 stat); @@ -262,64 +263,6 @@ UNIT com_unit[] = { { UDATA (&coms_svc, UNIT_DIS, 0), COMC_WAIT } }; -REG com_reg[] = { - { FLDATA (ENABLE, com_enab, 0) }, - { ORDATA (STATE, com_sta, 6) }, - { ORDATA (MSGNUM, com_msgn, 12) }, - { ORDATA (SNS, com_sns, 60) }, - { ORDATA (CHOB, com_chob, 36) }, - { FLDATA (CHOBV, com_chob_v, 0) }, - { FLDATA (STOP, com_stop, 0) }, - { BRDATA (BUF, com_buf, 8, 36, COM_BUFSIZ) }, - { DRDATA (BPTR, com_bptr, 7), REG_RO }, - { DRDATA (BLIM, com_blim, 7), REG_RO }, - { BRDATA (NRET, com_not_ret, 10, 32, COM_TLINES), REG_RO + PV_LEFT }, - { BRDATA (FREEQ, &com_free, 10, 16, 2) }, - { BRDATA (INPQ, &com_inpq, 10, 16, 2) }, - { BRDATA (OUTQ, com_outq, 10, 16, 2 * COM_TLINES) }, - { BRDATA (PKTB, com_pkt, 10, 16, 2 * COM_PKTSIZ) }, - { DRDATA (TTIME, com_unit[COM_CIU].wait, 24), REG_NZ + PV_LEFT }, - { DRDATA (WTIME, com_unit[COM_CHU].wait, 24), REG_NZ + PV_LEFT }, - { DRDATA (CHAN, com_ch, 3), REG_HRO }, - { NULL } - }; - -MTAB com_mod[] = { - { UNIT_ATT, UNIT_ATT, "summary", NULL, - NULL, &tmxr_show_summ, (void *) &com_desc }, - { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, - NULL, &tmxr_show_cstat, (void *) &com_desc }, - { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, - NULL, &tmxr_show_cstat, (void *) &com_desc }, - { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_FQ, "FREEQ", NULL, - NULL, &com_show_ctrl, 0 }, - { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_IQ, "INQ", NULL, - NULL, &com_show_ctrl, 0 }, - { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_OQ, "OUTQ", NULL, - NULL, &com_show_ctrl, 0 }, - { MTAB_XTD | MTAB_VDV | MTAB_NMO, -1, "ALL", NULL, - NULL, &com_show_ctrl, 0 }, - { MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "OUTQ", NULL, - NULL, &com_show_outq, 0 }, - { 0 } - }; - -DEVICE com_dev = { - "COM", com_unit, com_reg, com_mod, - 3, 10, 31, 1, 16, 8, - &tmxr_ex, &tmxr_dep, &com_reset, - NULL, &com_attach, &com_detach, - &com_dib, DEV_NET | DEV_DIS - }; - -/* COML data structures - - coml_dev COML device descriptor - coml_unit COML unit descriptor - coml_reg COML register list - coml_mod COML modifiers list -*/ - UNIT coml_unit[] = { { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, @@ -352,10 +295,70 @@ UNIT coml_unit[] = { { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, - { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&comto_svc, 0, 0), COML_WAIT }, }; +REG com_reg[] = { + { FLDATA (ENABLE, com_enab, 0) }, + { ORDATA (STATE, com_sta, 6) }, + { ORDATA (MSGNUM, com_msgn, 12) }, + { ORDATA (SNS, com_sns, 60) }, + { ORDATA (CHOB, com_chob, 36) }, + { FLDATA (CHOBV, com_chob_v, 0) }, + { FLDATA (STOP, com_stop, 0) }, + { ORDATA (QUIT, com_quit, 7) }, + { ORDATA (INTR, com_intr, 7) }, + { BRDATA (BUF, com_buf, 8, 36, COM_BUFSIZ) }, + { DRDATA (BPTR, com_bptr, 7), REG_RO }, + { DRDATA (BLIM, com_blim, 7), REG_RO }, + { BRDATA (NRET, com_not_ret, 10, 32, COM_TLINES), REG_RO + PV_LEFT }, + { URDATA (NEEDID, coml_unit[0].NEEDID, 8, 1, 0, COM_TLINES, 0) }, + { URDATA (NOECHO, coml_unit[0].NOECHO, 8, 1, 0, COM_TLINES, 0) }, + { URDATA (INPP, coml_unit[0].INPP, 8, 1, 0, COM_TLINES, 0) }, + { BRDATA (FREEQ, &com_free, 10, 16, 2) }, + { BRDATA (INPQ, com_inpq, 10, 16, 2 * COM_TLINES) }, + { BRDATA (OUTQ, com_outq, 10, 16, 2 * COM_TLINES) }, + { BRDATA (PKTB, com_pkt, 10, 16, 2 * COM_PKTSIZ) }, + { DRDATA (TTIME, com_unit[COM_CIU].wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (WTIME, com_unit[COM_CHU].wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (CHAN, com_ch, 3), REG_HRO }, + { NULL } + }; + +MTAB com_mod[] = { + { UNIT_ATT, UNIT_ATT, "summary", NULL, + NULL, &tmxr_show_summ, (void *) &com_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &tmxr_show_cstat, (void *) &com_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &tmxr_show_cstat, (void *) &com_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_FQ, "FREEQ", NULL, + NULL, &com_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_IQ, "INPQ", NULL, + NULL, &com_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_OQ, "OUTQ", NULL, + NULL, &com_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, -1, "ALL", NULL, + NULL, &com_show_ctrl, 0 }, + { 0 } + }; + +DEVICE com_dev = { + "COM", com_unit, com_reg, com_mod, + 3, 10, 31, 1, 16, 8, + &tmxr_ex, &tmxr_dep, &com_reset, + NULL, &com_attach, &com_detach, + &com_dib, DEV_NET | DEV_DIS + }; + +/* COML data structures + + coml_dev COML device descriptor + coml_unit COML unit descriptor + coml_reg COML register list + coml_mod COML modifiers list +*/ + MTAB coml_mod[] = { { UNIT_K35+UNIT_2741, 0 , "KSR-37", "KSR-37", NULL }, { UNIT_K35+UNIT_2741, UNIT_K35 , "KSR-35", "KSR-35", NULL }, @@ -366,6 +369,10 @@ MTAB coml_mod[] = { &tmxr_set_log, &tmxr_show_log, (void*) &com_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, (void *) &com_desc }, + { MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "INPQ", NULL, + NULL, &com_show_oneq, 0 }, + { MTAB_XTD | MTAB_VUN | MTAB_NMO, 1, "OUTQ", NULL, + NULL, &com_show_oneq, 0 }, { 0 } }; @@ -441,7 +448,7 @@ switch (com_sta) { /* case on state */ com_sns &= ~COMS_DYN; /* clear dynamic flags */ if (com_free.head) /* free space? */ com_set_sns (COMS_INBF); - if (com_inpq.head) /* pending input? */ + if (com_test_inp ()) /* pending input? */ com_set_sns (COMS_DATR); com_buf[0] = (com_sns >> 24) & DMASK; /* buffer is 2 words */ com_buf[1] = (com_sns << 12) & DMASK; @@ -478,8 +485,8 @@ return SCPE_OK; t_stat comc_svc (UNIT *uptr) { -uint32 i, j, k, ccnt, ln, uln, ent; -uint16 chr; +uint32 i, j, k, ccnt, ln, uln; +uint16 chr, ent; t_uint64 dat; switch (com_sta) { /* case on state */ @@ -489,15 +496,27 @@ switch (com_sta) { /* case on state */ com_buf[i] = 0; com_buf[0] = com_msgn; /* 1st char is msg num */ com_msgn = (com_msgn + 1) & 03777; /* incr msg num */ - for (i = 1, j = 0; i < COMI_12BMAX; i++) { /* fill buffer */ - ent = com_gethd_free (&com_inpq); /* get next entry */ - if (ent == 0) /* q empty, done */ - break; - if ((i % 3) == 0) /* next word? */ - j++; - com_buf[j] = (com_buf[j] << 12) | /* pack data */ - ((t_uint64) (com_pkt[ent].data & 07777)); - } + for (i = 1, j = 0, ln = 0; /* check all lines */ + (ln < COM_TLINES) && (i < COMI_12BMAX); /* until buffer full */ + ln++) { + chr = (uint16) com_gen_ccmp (ln); /* completion msg? */ + if ((chr == 0) && coml_unit[ln].INPP) { /* no, line input? */ + ent = com_gethd_free (&com_inpq[ln]); /* get first char */ + if (ent != 0) /* any input? */ + chr = com_pkt[ent].data; /* return char */ + else coml_unit[i].INPP = 0; /* this line is idle */ + } /* end if input pending */ + if (chr != 0) { /* got something? */ + if ((i++ % 3) == 0) /* next word? */ + j++; + com_buf[j] = (com_buf[j] << 12) | /* pack line number */ + ((t_uint64) ((ln + COM_LBASE) | COMI_VALIDL)); + if ((i++ % 3) == 0) /* next word? */ + j++; + com_buf[j] = (com_buf[j] << 12) | /* pack data */ + ((t_uint64) (chr & 07777)); + } /* end if char */ + } /* end for buffer */ for (k = i % 3; k < 3; k++) { /* fill with EOM */ if (k == 0) /* next word? */ j++; @@ -519,8 +538,11 @@ switch (com_sta) { /* case on state */ break; case CHSL_RDS|CHSL_3RD: /* read end */ - if (com_qdone (com_ch)) /* done? */ - return com_test_atn (com_ch); /* test atn, exit */ + if (com_qdone (com_ch)) { /* done? */ + if (com_test_inp ()) /* more data waiting? */ + ch9_set_atn (com_ch); + return SCPE_OK; /* exit */ + } com_sta = CHSL_RDS; /* repeat sequence */ break; @@ -545,7 +567,8 @@ switch (com_sta) { /* case on state */ ln = COMO_GETLN (dat); /* line number */ if (ln >= (COM_TLINES + COM_LBASE)) /* invalid line? */ return STOP_INVLIN; - if (dat & COMO_CTLRST) /* char must be 0 */ + chr = (uint16) ((dat >> 12) & 07777); /* control message */ + if (chr != COMO_CTLRST) /* char must be 0 */ return STOP_INVMSG; if (ln >= COM_LBASE) com_reset_ln (ln - COM_LBASE); @@ -593,7 +616,7 @@ switch (com_sta) { /* case on state */ case CHSL_WRS|CHSL_4TH: /* buffer done */ if (com_qdone (com_ch)) /* done? */ - return com_test_atn (com_ch); /* test atn, exit */ + return SCPE_OK; /* exit */ com_sta = CHSL_WRS; /* repeat sequence */ break; @@ -609,8 +632,8 @@ return SCPE_OK; t_stat comti_svc (UNIT *uptr) { -int32 c; -t_stat r; +int32 c, ln = COM_MLINES; +uint16 ent; sim_activate (uptr, uptr->wait); /* continue poll */ c = sim_poll_kbd (); /* get character */ @@ -618,17 +641,25 @@ if (c && !(c & (SCPE_BREAK|SCPE_KFLAG))) /* error? */ return c; if (!com_enab || (c & SCPE_BREAK)) /* !enab, break? done */ return SCPE_OK; -if (coml_unit[COM_MLINES].NEEDID) /* ID needed? */ - return com_send_id (COM_MLINES); +if (coml_unit[ln].NEEDID) /* ID needed? */ + return com_send_id (ln); if ((c & SCPE_KFLAG) && ((c = c & 0177) != 0)) { /* char input? */ - if (r = com_queue_in (COM_MLINES, c)) - return r; - if (sim_tt_outcvt (c, TT_MODE_7P) >= 0) - sim_putchar (c); - if (c == '\r') - sim_putchar ('\n'); + if ((c == 0177) || (c == '\b')) { /* delete? */ + ent = com_gettl_free (&com_inpq[ln]); /* remove last char */ + if (!coml_unit[ln].NOECHO) + sim_putchar (ent? '\b': '\a'); + return SCPE_OK; + } + if (!com_queue_in (ln, c)) /* add to inp queue */ + return STOP_NOIFREE; + if (!coml_unit[ln].NOECHO) { /* echo enabled? */ + if (sim_tt_outcvt (c, TT_MODE_7P) >= 0) /* printable? */ + sim_putchar (c); + if (c == '\r') /* line end? */ + sim_putchar ('\n'); + } } -return com_test_atn (com_ch); /* set ATN if input */ +return SCPE_OK; /* set ATN if input */ } /* Unit service - receive side @@ -639,7 +670,7 @@ return com_test_atn (com_ch); /* set ATN if input */ t_stat comi_svc (UNIT *uptr) { int32 c, ln, t; -t_stat r; +uint16 ent; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_OK; @@ -652,6 +683,8 @@ if (ln >= 0) { /* got one? */ com_ldsc[ln].rcve = 1; /* rcv enabled */ coml_unit[ln].CONN = 1; /* flag connected */ coml_unit[ln].NEEDID = 1; /* need ID */ + coml_unit[ln].NOECHO = 0; /* echo enabled */ + coml_unit[ln].INPP = 0; /* no input pending */ } tmxr_poll_rx (&com_desc); /* poll for input */ for (ln = 0; ln < COM_MLINES; ln++) { /* loop thru mux */ @@ -661,13 +694,21 @@ for (ln = 0; ln < COM_MLINES; ln++) { /* loop thru mux */ c = tmxr_getc_ln (&com_ldsc[ln]); /* get char */ if (c) { /* any char? */ c = c & 0177; /* mask to 7b */ - if (r = com_queue_in (ln, c)) /* queue char, err? */ - return r; + if ((c == 0177) || (c == '\b')) { /* delete? */ + ent = com_gettl_free (&com_inpq[ln]); /* remove last char */ + if (!coml_unit[ln].NOECHO) + tmxr_putc_ln (&com_ldsc[ln], ent? '\b': '\a'); + return SCPE_OK; + } + if (!com_queue_in (ln, c)) /* queue char, err? */ + return STOP_NOIFREE; if (com_ldsc[ln].xmte) { /* output enabled? */ - if (sim_tt_outcvt (c, TT_MODE_7P) >= 0) /* echo char */ - tmxr_putc_ln (&com_ldsc[ln], c); - if (c == '\r') /* add LF after CR */ - tmxr_putc_ln (&com_ldsc[ln], '\n'); + if (!coml_unit[ln].NOECHO) { /* echo enabled? */ + if (sim_tt_outcvt (c, TT_MODE_7P) >= 0) + tmxr_putc_ln (&com_ldsc[ln], c); + if (c == '\r') /* add LF after CR */ + tmxr_putc_ln (&com_ldsc[ln], '\n'); + } tmxr_poll_tx (&com_desc); /* poll xmt */ } /* end if enabled */ } /* end if char */ @@ -675,29 +716,29 @@ for (ln = 0; ln < COM_MLINES; ln++) { /* loop thru mux */ else if (coml_unit[ln].CONN) { /* not conn, was conn? */ coml_unit[ln].CONN = 0; /* clear connected */ coml_unit[ln].NEEDID = 0; /* clear need id */ - if (!com_inp_msg (ln, COMI_HANGUP)) /* hangup message */ + com_set_inpp (ln); /* input pending, ATN1 */ + if (!com_new_puttl (&com_inpq[ln], COMI_HANGUP))/* hangup message */ return STOP_NOIFREE; } } /* end for */ -return com_test_atn (com_ch); /* set ATN if input */ +return SCPE_OK; } /* Unit service - console transmit */ t_stat comto_svc (UNIT *uptr) { +uint32 ln = COM_MLINES; uint32 c, c1; -if (com_outq[COM_MLINES].head == 0) /* no more characters? */ - return com_send_ccmp (COM_MLINES); /* free any remaining */ -c = com_queue_out (COM_MLINES, &c1); /* get character, cvt */ +c = com_queue_out (ln, &c1); /* get character, cvt */ if (c) /* printable? output */ sim_putchar (c); if (c1) /* second char? output */ sim_putchar (c1); -sim_activate (uptr, uptr->wait); /* next char */ -if (com_not_ret[COM_MLINES] >= COMI_CMAX) /* completion needed? */ - return com_send_ccmp (COM_MLINES); /* generate msg */ +if (com_outq[ln].head == 0) /* line idle? */ + ch9_set_atn (com_ch); /* set ATN1 */ +else sim_activate (uptr, uptr->wait); /* next char */ return SCPE_OK; } @@ -708,8 +749,6 @@ t_stat como_svc (UNIT *uptr) uint32 c, c1; int32 ln = uptr - coml_unit; /* line # */ -if (com_outq[ln].head == 0) /* no more characters? */ - return com_send_ccmp (ln); /* free any remaining */ if (com_ldsc[ln].conn) { /* connected? */ if (com_ldsc[ln].xmte) { /* output enabled? */ c = com_queue_out (ln, &c1); /* get character, cvt */ @@ -719,9 +758,9 @@ if (com_ldsc[ln].conn) { /* connected? */ tmxr_putc_ln (&com_ldsc[ln], c1); } /* end if */ tmxr_poll_tx (&com_desc); /* poll xmt */ - sim_activate (uptr, uptr->wait); /* next char */ - if (com_not_ret[ln] >= COMI_CMAX) /* completion needed? */ - return com_send_ccmp (ln); /* generate msg */ + if (com_outq[ln].head == 0) /* line idle? */ + ch9_set_atn (com_ch); /* set ATN1 */ + else sim_activate (uptr, uptr->wait); /* next char */ } /* end if conn */ return SCPE_OK; } @@ -730,32 +769,39 @@ return SCPE_OK; t_stat com_send_id (uint32 ln) { -com_inp_msg (ln, COMI_DIALUP); /* input message: */ +com_new_puttl (&com_inpq[ln], COMI_DIALUP); /* input message: */ if (coml_unit[ln].flags & UNIT_K35) /* dialup, ID, endID */ - com_inp_msg (ln, COMI_K35); -else com_inp_msg (ln, COMI_K37); -com_inp_msg (ln, 0); -com_inp_msg (ln, 0); -com_inp_msg (ln, 0); -com_inp_msg (ln, 0); -com_inp_msg (ln, (uint16) (ln + COM_LBASE)); -if (!com_inp_msg (ln, COMI_ENDID)) /* make sure there */ + com_new_puttl (&com_inpq[ln], COMI_K35); +else com_new_puttl (&com_inpq[ln], COMI_K37); +com_new_puttl (&com_inpq[ln], 0); +com_new_puttl (&com_inpq[ln], 0); +com_new_puttl (&com_inpq[ln], 0); +com_new_puttl (&com_inpq[ln], 0); +com_new_puttl (&com_inpq[ln], (uint16) (ln + COM_LBASE)); +if (!com_new_puttl (&com_inpq[ln], COMI_ENDID)) /* make sure there */ return STOP_NOIFREE; /* was room for msg */ coml_unit[ln].NEEDID = 0; +com_set_inpp (ln); /* input pending, ATN1 */ return SCPE_OK; } /* Translate and queue input character */ -t_stat com_queue_in (uint32 ln, uint32 c) +t_bool com_queue_in (uint32 ln, uint32 c) { uint16 out; -if (c == com_intr) +if (c == com_intr) { out = COMI_INTR; -else if (c == com_quit) + com_set_inpp (ln); + } +else if (c == com_quit) { out = COMI_QUIT; + com_set_inpp (ln); + } else { + if (c == '\r') + com_set_inpp (ln); if (coml_unit[ln].flags & UNIT_K35) { /* KSR-35? */ if (islower (c)) /* convert LC to UC */ c = toupper (c); @@ -763,9 +809,7 @@ else { else c |= (com_epar[c]? COMI_PARITY: 0); /* add even parity */ out = (~c) & 0377; /* 1's complement */ } -if (!com_inp_msg (ln, out)) /* input message */ - return STOP_NOIFREE; -return SCPE_OK; +return com_new_puttl (&com_inpq[ln], out); /* input message */ } /* Retrieve and translate output character */ @@ -775,20 +819,18 @@ uint32 com_queue_out (uint32 ln, uint32 *c1) uint32 c, ent, raw; *c1 = 0; /* assume non-printing */ -ent = com_gethd_free (&com_outq[ln]); /* get character */ -if (ent == 0) /* nothing? */ - return 0; +if ((ent = com_gethd_free (&com_outq[ln])) == 0) /* get character */ + return 0; /* nothing, exit */ raw = com_pkt[ent].data; /* get 12b character */ com_not_ret[ln]++; if (raw == COMO_BITRPT) { /* insert delay? */ - com_skip_outc (ln); + if (com_gethd_free (&com_outq[ln])) /* skip next char */ + com_not_ret[ln]++; /* and count it */ return 0; } c = (~raw >> 1) & 0177; /* remove start, parity */ -if (c >= 040) { /* printable? */ - if (c == 0177) /* DEL? ignore */ - return 0; - if ((coml_unit[ln].flags & UNIT_K35) && islower (c)) /* KSR-35 LC? */ +if ((c >= 040) && (c != 0177)) { /* printable? */ + if ((coml_unit[ln].flags & UNIT_K35) && islower (c))/* KSR-35 LC? */ c = toupper (c); /* cvt to UC */ return c; } @@ -798,28 +840,20 @@ switch (c) { return c; case '\r': /* carriage return? */ - if (coml_unit[ln].flags & UNIT_K35) /* KSR-35? */ - *c1 = '\n'; /* lf after cr */ + *c1 = '\n'; /* lf after cr */ return c; case '\n': /* line feed? */ - if (!(coml_unit[ln].flags & UNIT_K35)) { /* KSR-37? */ - *c1 = '\n'; /* lf after cr */ - return '\r'; - } - return c; /* print lf */ + *c1 = '\n'; /* lf after cr */ + return '\r'; case 022: /* DC2 */ - if (!(com_unit[ln].flags & UNIT_K35)) { /* KSR-37? */ - com_skip_outc (ln); /* skip next */ - return '\n'; /* print lf */ - } - break; + coml_unit[ln].NOECHO = 1; + return 0; - case 023: /* DC3 */ - if (!(com_unit[ln].flags & UNIT_K35)) /* KSR-37? */ - com_skip_outc (ln); /* skip next */ - break; + case 024: /* DC4 */ + coml_unit[ln].NOECHO = 0; + return 0; } return 0; /* ignore others */ @@ -827,27 +861,17 @@ return 0; /* ignore others */ /* Generate completion message, if needed */ -t_stat com_send_ccmp (uint32 ln) +uint32 com_gen_ccmp (uint32 ln) { uint32 t; -if (t = com_not_ret[ln]) { /* chars not returned? */ +if ((t = com_not_ret[ln]) != 0) { /* chars not returned? */ if (t > COMI_CMAX) /* limit to max */ t = COMI_CMAX; com_not_ret[ln] -= t; /* keep count */ - if (!com_inp_msg (ln, COMI_COMP (t))) /* gen completion msg */ - return STOP_NOIFREE; + return COMI_COMP (t); /* gen completion msg */ } -return SCPE_OK; -} - -/* Skip next char in output queue */ - -void com_skip_outc (uint32 ln) -{ -if (com_gethd_free (&com_outq[ln])) /* count it */ - com_not_ret[ln]++; -return; +return 0; } /* Read and validate output buffer */ @@ -863,13 +887,26 @@ else if (!com_stop) { /* not stopped? */ return com_chob; } -/* Set attention if input pending */ +/* Test whether input pending */ -t_stat com_test_atn (uint32 ch) +t_bool com_test_inp (void) { -if (com_inpq.head) - ch9_set_atn (ch); -return SCPE_OK; +uint32 i; + +for (i = 0; i < COM_TLINES; i++) { + if ((com_not_ret[i] != 0) || coml_unit[i].INPP) + return TRUE; + } +return FALSE; +} + +/* Set input pending and attention */ + +void com_set_inpp (uint32 ln) +{ +coml_unit[ln].INPP = 1; +ch9_set_atn (com_ch); +return; } /* Test for done */ @@ -904,6 +941,17 @@ if ((ent = com_gethd (lh)) != 0) return ent; } +/* Remove from tail and free */ + +uint16 com_gettl_free (LISTHD *lh) +{ +uint16 ent; + +if ((ent = com_gethd (lh)) != 0) + com_puttl (&com_free, ent); +return ent; +} + /* Get free entry and insert at tail */ t_bool com_new_puttl (LISTHD *lh, uint16 val) @@ -933,6 +981,29 @@ else lh->tail = 0; return ent; } +/* Remove from tail */ + +uint16 com_gettl (LISTHD *lh) +{ +uint16 ent, next; +uint32 i; + +ent = lh->tail; +if (lh->head == lh->tail) { + lh->head = lh->tail = 0; + return ent; + } +next = lh->head; +for (i = 0; i < COM_PKTSIZ; i++) { + if (com_pkt[next].next == ent) { + com_pkt[next].next = 0; + lh->tail = next; + return ent; + } + } +return 0; +} + /* Insert at tail */ void com_puttl (LISTHD *lh, uint16 ent) @@ -945,25 +1016,6 @@ lh->tail = ent; return; } -/* Insert line and message into input queue */ - -t_bool com_inp_msg (uint32 ln, uint16 msg) -{ -uint16 ent1, ent2; - -if ((ent1 = com_gethd (&com_free)) != 0) { /* pkt avail? */ - if ((ent2 = com_gethd (&com_free)) != 0) { /* 2nd pkt avail? */ - com_pkt[ent1].data = (ln + COM_LBASE) | COMI_VALIDL; /* 1st pkt = line# */ - com_pkt[ent2].data = msg; /* 2nd pkt = char */ - com_puttl (&com_inpq, ent1); /* queue pkts */ - com_puttl (&com_inpq, ent2); - return TRUE; - } - com_puttl (&com_free, ent1); /* free 1st */ - } -return FALSE; /* failed */ -} - /* Set flag in sense */ void com_set_sns (t_uint64 stat) @@ -1011,9 +1063,9 @@ com_bptr = 0; com_blim = 0; for (i = 0; i < COM_BUFSIZ; i++) com_buf[i] = 0; -com_inpq.head = 0; /* init queues */ -com_inpq.tail = 0; -for (i = 0; i < COM_TLINES; i++) { +for (i = 0; i < COM_TLINES; i++) { /* init lines */ + com_inpq[i].head = 0; + com_inpq[i].tail = 0; com_outq[i].head = 0; com_outq[i].tail = 0; com_reset_ln (i); @@ -1063,8 +1115,12 @@ return r; void com_reset_ln (uint32 ln) { +while (com_gethd_free (&com_inpq[ln])) ; while (com_gethd_free (&com_outq[ln])) ; com_not_ret[ln] = 0; +coml_unit[ln].NEEDID = 0; +coml_unit[ln].NOECHO = 0; +coml_unit[ln].INPP = 0; sim_cancel (&coml_unit[ln]); if ((ln < COM_MLINES) && (com_ldsc[ln].conn == 0)) coml_unit[ln].CONN = 0; @@ -1084,7 +1140,7 @@ for (i = 0; i < COM_PKTSIZ; i++) { fprintf (st, "%s is empty\n", name); else if (i == 1) fprintf (st, "%s has 1 entry\n", name); - else fprintf (st, "%s had %d entries\n", name, i); + else fprintf (st, "%s has %d entries\n", name, i); return i; } next = com_pkt[next].next; @@ -1110,44 +1166,21 @@ com_show_qsumm (st, &com_free, "Free queue"); return SCPE_OK; } -t_stat com_show_inq (FILE *st, UNIT *uptr, int32 val, void *desc) -{ -uint32 entc, ln, i, next; - -if (entc = com_show_qsumm (st, &com_inpq, "Input queue")) { - for (i = 0, next = com_inpq.head; next != 0; - i++, next = com_pkt[next].next) { - if ((i % 4) == 0) - fprintf (st, "%d:\t", i); - ln = com_pkt[next].data; - next = com_pkt[next].next; - if (next == 0) { - fprintf (st, "Line number without data\n"); - return SCPE_OK; - } - fprintf (st, "%d/", ln); - com_show_char (st, com_pkt[next].data); - fputc ((((i % 4) == 3)? '\n': '\t'), st); - } - if (i % 4) - fputc ('\n', st); - } -return SCPE_OK; -} - -t_stat com_show_outq (FILE *st, UNIT *uptr, int32 val, void *desc) +t_stat com_show_oneq (FILE *st, UNIT *uptr, int32 val, void *desc) { uint32 entc, ln, i, next; +LISTHD *lh; char name[20]; -ln = uptr - com_dev.units; -sprintf (name, "Output queue %d", ln); -if (entc = com_show_qsumm (st, &com_outq[ln], name)) { - for (i = 0, next = com_outq[ln].head; next != 0; +ln = uptr - coml_dev.units; +sprintf (name, val? "Output queue %d": "Input queue %d", ln); +lh = val? &com_outq[ln]: &com_inpq[ln]; +if (entc = com_show_qsumm (st, lh, name)) { + for (i = 0, next = lh->head; next != 0; i++, next = com_pkt[next].next) { if ((i % 8) == 0) fprintf (st, "%d:\t", i); - com_show_char (st, com_pkt[next].data >> 1); + com_show_char (st, com_pkt[next].data >> (val? 1: 0)); fputc ((((i % 8) == 7)? '\n': '\t'), st); } if (i % 8) @@ -1156,12 +1189,12 @@ if (entc = com_show_qsumm (st, &com_outq[ln], name)) { return SCPE_OK; } -t_stat com_show_aoutq (FILE *st, UNIT *uptr, int32 val, void *desc) +t_stat com_show_allq (FILE *st, UNIT *uptr, int32 val, void *desc) { uint32 i; for (i = 0; i < COM_TLINES; i++) - com_show_outq (st, com_dev.units + i, 1, desc); + com_show_oneq (st, coml_dev.units + i, val, desc); return SCPE_OK; } @@ -1170,10 +1203,10 @@ t_stat com_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc) if (!com_enab) fprintf (st, "Controller is not initialized\n"); if (val & COMR_FQ) - com_show_freeq (st, uptr, 1, desc); + com_show_freeq (st, uptr, 0, desc); if (val & COMR_IQ) - com_show_inq (st, uptr, 1, desc); + com_show_allq (st, uptr, 0, desc); if (val & COMR_OQ) - com_show_aoutq (st, uptr, 1, desc); + com_show_allq (st, uptr, 1, desc); return SCPE_OK; } diff --git a/I7094/i7094_com_old.c b/I7094/i7094_com_old.c new file mode 100644 index 00000000..4a665453 --- /dev/null +++ b/I7094/i7094_com_old.c @@ -0,0 +1,1179 @@ +/* i7094_com.c: IBM 7094 7750 communications interface simulator + + Copyright (c) 2005-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + com 7750 controller + coml 7750 lines + + 19-Nov-2008 RMS Revised for common TMXR show routines + + This module implements an abstract simulator for the IBM 7750 communications + computer as used by the CTSS system. The 7750 supports up to 112 lines; + the simulator supports 33. The 7750 can handle both high-speed lines, in + 6b and 12b mode, and normal terminals, in 12b mode only; the simulator + supports only terminals. The 7750 can handle many different kinds of + terminals; the simulator supports only a limited subset. + + Input is asynchronous. The 7750 sets ATN1 to signal availability of input. + When the 7094 issues a CTLRN, the 7750 gathers available input characters + into a message. The message has a 12b sequence number, followed by 12b line + number/character pairs, followed by end-of-medium (03777). Input characters + can either be control characters (bit 02000 set) or data characters. Data + characters are 1's complemented and are 8b wide: 7 data bits and 1 parity + bit (which may be 0). + + Output is synchronous. When the 7094 issues a CTLWN, the 7750 interprets + the channel output as a message. The message has a 12b line number, followed + by a 12b character count, followed by characters, followed by end-of-medium. + If bit 02000 of the line number is set, the characters are 12b wide. If + bit 01000 is set, the message is a control message. 12b characters consist + of 7 data bits, 1 parity bit, and 1 start bit. Data characters are 1's + complemented. Data character 03777 is special and causes the 7750 to + repeat the previous bit for the number of bit times specified in the next + character. This is used to generate delays for positioning characters. + + The 7750 supports flow control for output. To help the 7094 account for + usage of 7750 buffer memory, the 7750 sends 'character output completion' + messages for every 'n' characters output on a line, where n <= 31. + + Note that the simulator console is mapped in as line n+1. +*/ + +#include "i7094_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include + +#define COM_MLINES 32 /* mux lines */ +#define COM_TLINES (COM_MLINES + 1) /* total lines */ +#define COM_BUFSIZ 120 /* max chan transfer */ +#define COM_PKTSIZ 16384 /* character buffer */ + +#define UNIT_V_2741 (TTUF_V_UF + 0) /* 2741 - ni */ +#define UNIT_V_K35 (TTUF_V_UF + 1) /* KSR-35 */ +#define UNIT_2741 (1 << UNIT_V_2741) +#define UNIT_K35 (1 << UNIT_V_K35) + +#define CONN u3 /* line is connected */ +#define NEEDID u4 /* need to send ID */ + +#define COM_INIT_POLL 8000 /* polling interval */ +#define COMC_WAIT 2 /* channel delay time */ +#define COML_WAIT 1000 /* char delay time */ +#define COM_LBASE 4 /* start of lines */ + +/* Input threads */ + +#define COM_PLU 0 /* multiplexor poll */ +#define COM_CIU 1 /* console input */ +#define COM_CHU 2 /* channel transfer */ +#define COM_SNS 3 /* sense transfer */ + +/* Communications input */ + +#define COMI_VALIDL 02000 /* valid line flag */ +#define COMI_PARITY 00200 /* parity bit */ +#define COMI_DIALUP 02001 /* dialup */ +#define COMI_ENDID 02002 /* end ID */ +#define COMI_INTR 02003 /* interrupt */ +#define COMI_QUIT 02004 /* quit */ +#define COMI_HANGUP 02005 /* hangup */ +#define COMI_EOM 03777 /* end of medium */ +#define COMI_COMP(x) ((uint16) (03000 + ((x) & COMI_CMAX))) +#define COMI_K35 1 /* KSR-35 ID */ +#define COMI_K37 7 /* KSR-37 ID */ +#define COMI_2741 8 /* 2741 ID */ +#define COMI_CMAX 31 /* max chars returned */ +#define COMI_BMAX 50 /* buffer max, words */ +#define COMI_12BMAX ((3 * COMI_BMAX) - 1) /* last 12b char */ + +/* Communications output */ + +#define COMO_LIN12B 0200000000000 /* line is 12b */ +#define COMO_LINCTL 0100000000000 /* control msg */ +#define COMO_GETLN(x) (((uint32) ((x) >> 24)) & 0777) +#define COMO_CTLRST 0000077770000 /* control reset */ +#define COMO_BITRPT 03777 /* bit repeat */ +#define COMO_EOM12B 07777 /* end of medium */ +#define COMO_BMAX 94 /* buffer max, words */ +#define COMO_12BMAX ((3 * COMO_BMAX) - 1) + +/* Status word (60b) */ + +#define COMS_PCHK 004000000000000000000 /* prog check */ +#define COMS_DCHK 002000000000000000000 /* data check */ +#define COMS_EXCC 001000000000000000000 /* exc cond */ +#define COMS_MLNT 000040000000000000000 /* message length check */ +#define COMS_CHNH 000020000000000000000 /* channel hold */ +#define COMS_CHNQ 000010000000000000000 /* channel queue full */ +#define COMS_ITMO 000000100000000000000 /* interface timeout */ +#define COMS_DATR 000000004000000000000 /* data message ready */ +#define COMS_INBF 000000002000000000000 /* input buffer free */ +#define COMS_SVCR 000000001000000000000 /* service message ready */ +#define COMS_PALL 000000000000000000000 +#define COMS_DALL 000000000000000000000 +#define COMS_EALL 000000000000000000000 +#define COMS_DYN 000000007000000000000 + +/* Report variables */ + +#define COMR_FQ 1 /* free queue */ +#define COMR_IQ 2 /* input queue */ +#define COMR_OQ 4 /* output queue */ + +/* List heads and entries */ + +typedef struct { + uint16 head; + uint16 tail; + } LISTHD; + +typedef struct { + uint16 next; + uint16 data; + } LISTENT; + +/* The 7750 character buffer is maintained as linked lists. The lists are: + + free free list + inpq input queue + outq[ln] output queue for line ln + + The input queue has two entries for each character; the first is the + line number, the second the character. The output queues have only + one entry for each character. + + Links are done as subscripts in array com_pkt. This allows the list + headers and the queues themselves to be saved and restored. */ + +uint32 com_ch = CH_E; /* saved channel */ +uint32 com_enab = 0; /* 7750 enabled */ +uint32 com_msgn = 0; /* next input msg num */ +uint32 com_sta = 0; /* 7750 state */ +uint32 com_stop = 0; /* channel stop */ +uint32 com_quit = 0; /* quit code */ +uint32 com_intr = 0; /* interrupt code */ +uint32 com_bptr = 0; /* buffer pointer */ +uint32 com_blim = 0; /* buffer count */ +uint32 com_tps = 50; /* polls/second */ +uint32 com_not_ret[COM_TLINES] = { 0 }; /* chars not returned */ +t_uint64 com_sns = 0; /* sense word */ +t_uint64 com_chob = 0; /* chan output buf */ +uint32 com_chob_v = 0; /* valid flag */ +t_uint64 com_buf[COM_BUFSIZ]; /* channel buffer */ +LISTHD com_free; /* free list */ +LISTHD com_inpq; /* input queue */ +LISTHD com_outq[COM_TLINES]; /* output queue */ +LISTENT com_pkt[COM_PKTSIZ]; /* character packets */ +TMLN com_ldsc[COM_MLINES] = { 0 }; /* line descriptors */ +TMXR com_desc = { COM_MLINES, 0, 0, com_ldsc }; /* mux descriptor */ + +/* Even parity truth table */ + +static const uint8 com_epar[128] = { + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1 + }; + +extern uint32 ch_req; + +t_stat com_chsel (uint32 ch, uint32 sel, uint32 unit); +t_stat com_chwr (uint32 ch, t_uint64 val, uint32 flags); +t_stat comi_svc (UNIT *uptr); +t_stat comc_svc (UNIT *uptr); +t_stat como_svc (UNIT *uptr); +t_stat coms_svc (UNIT *uptr); +t_stat comti_svc (UNIT *uptr); +t_stat comto_svc (UNIT *uptr); +t_stat com_reset (DEVICE *dptr); +t_stat com_attach (UNIT *uptr, char *cptr); +t_stat com_detach (UNIT *uptr); +t_stat com_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat com_show_freeq (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat com_show_inq (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat com_show_aoutq (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat com_show_outq (FILE *st, UNIT *uptr, int32 val, void *desc); +void com_reset_ln (uint32 i); +uint16 com_gethd_free (LISTHD *lh); +uint16 com_gethd (LISTHD *lh); +t_bool com_new_puttl (LISTHD *lh, uint16 val); +void com_puttl (LISTHD *lh, uint16 ent); +t_bool com_inp_msg (uint32 ln, uint16 msg); +void com_skip_outc (uint32 ln); +t_stat com_test_atn (uint32 ch); +t_uint64 com_getob (uint32 ch); +t_bool com_qdone (uint32 ch); +void com_end (uint32 ch, uint32 fl, uint32 st); +t_stat com_send_id (uint32 ln); +t_stat com_send_ccmp (uint32 ln); +t_stat com_queue_in (uint32 ln, uint32 ch); +uint32 com_queue_out (uint32 ln, uint32 *c1); +void com_set_sns (t_uint64 stat); + +/* COM data structures + + com_dev COM device descriptor + com_unit COM unit descriptor + com_reg COM register list + com_mod COM modifiers list +*/ + +DIB com_dib = { &com_chsel, &com_chwr }; + +UNIT com_unit[] = { + { UDATA (&comi_svc, UNIT_ATTABLE, 0), COM_INIT_POLL }, + { UDATA (&comti_svc, UNIT_DIS, 0), KBD_POLL_WAIT }, + { UDATA (&comc_svc, UNIT_DIS, 0), COMC_WAIT }, + { UDATA (&coms_svc, UNIT_DIS, 0), COMC_WAIT } + }; + +REG com_reg[] = { + { FLDATA (ENABLE, com_enab, 0) }, + { ORDATA (STATE, com_sta, 6) }, + { ORDATA (MSGNUM, com_msgn, 12) }, + { ORDATA (SNS, com_sns, 60) }, + { ORDATA (CHOB, com_chob, 36) }, + { FLDATA (CHOBV, com_chob_v, 0) }, + { FLDATA (STOP, com_stop, 0) }, + { BRDATA (BUF, com_buf, 8, 36, COM_BUFSIZ) }, + { DRDATA (BPTR, com_bptr, 7), REG_RO }, + { DRDATA (BLIM, com_blim, 7), REG_RO }, + { BRDATA (NRET, com_not_ret, 10, 32, COM_TLINES), REG_RO + PV_LEFT }, + { BRDATA (FREEQ, &com_free, 10, 16, 2) }, + { BRDATA (INPQ, &com_inpq, 10, 16, 2) }, + { BRDATA (OUTQ, com_outq, 10, 16, 2 * COM_TLINES) }, + { BRDATA (PKTB, com_pkt, 10, 16, 2 * COM_PKTSIZ) }, + { DRDATA (TTIME, com_unit[COM_CIU].wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (WTIME, com_unit[COM_CHU].wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (CHAN, com_ch, 3), REG_HRO }, + { NULL } + }; + +MTAB com_mod[] = { + { UNIT_ATT, UNIT_ATT, "summary", NULL, + NULL, &tmxr_show_summ, (void *) &com_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &tmxr_show_cstat, (void *) &com_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &tmxr_show_cstat, (void *) &com_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_FQ, "FREEQ", NULL, + NULL, &com_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_IQ, "INQ", NULL, + NULL, &com_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_OQ, "OUTQ", NULL, + NULL, &com_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, -1, "ALL", NULL, + NULL, &com_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "OUTQ", NULL, + NULL, &com_show_outq, 0 }, + { 0 } + }; + +DEVICE com_dev = { + "COM", com_unit, com_reg, com_mod, + 3, 10, 31, 1, 16, 8, + &tmxr_ex, &tmxr_dep, &com_reset, + NULL, &com_attach, &com_detach, + &com_dib, DEV_NET | DEV_DIS + }; + +/* COML data structures + + coml_dev COML device descriptor + coml_unit COML unit descriptor + coml_reg COML register list + coml_mod COML modifiers list +*/ + +UNIT coml_unit[] = { + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&comto_svc, 0, 0), COML_WAIT }, + }; + +MTAB coml_mod[] = { + { UNIT_K35+UNIT_2741, 0 , "KSR-37", "KSR-37", NULL }, + { UNIT_K35+UNIT_2741, UNIT_K35 , "KSR-35", "KSR-35", NULL }, +// { UNIT_K35+UNIT_2741, UNIT_2741, "2741", "2741", NULL }, + { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, (void *) &com_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", + &tmxr_set_log, &tmxr_show_log, (void*) &com_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", + &tmxr_set_nolog, NULL, (void *) &com_desc }, + { 0 } + }; + +REG coml_reg[] = { + { URDATA (TIME, coml_unit[0].wait, 10, 24, 0, + COM_TLINES, REG_NZ + PV_LEFT) }, + { NULL } + }; + +DEVICE coml_dev = { + "COML", coml_unit, coml_reg, coml_mod, + COM_TLINES, 10, 31, 1, 16, 8, + NULL, NULL, &com_reset, + NULL, NULL, NULL, + NULL, DEV_DIS + }; + +/* COM: channel select */ + +t_stat com_chsel (uint32 ch, uint32 sel, uint32 unit) +{ +com_ch = ch; /* save channel */ +if (sim_is_active (&com_unit[COM_CHU]) || /* not idle? */ + sim_is_active (&com_unit[COM_SNS])) { + com_end (ch, CHINT_SEQC, 0); /* end, seq check */ + return SCPE_OK; + } + +switch (sel) { /* case on select */ + + case CHSL_RDS: /* read */ + case CHSL_WRS: /* write */ + com_sns = 0; /* clear status */ + sim_activate (&com_unit[COM_CHU], com_unit[COM_CHU].wait); + break; + + case CHSL_SNS: /* sense */ + sim_activate (&com_unit[COM_SNS], com_unit[COM_SNS].wait); + break; + + case CHSL_CTL: /* control */ + default: /* other */ + return STOP_ILLIOP; + } + +com_stop = 0; /* clear stop */ +com_sta = sel; /* set initial state */ +return SCPE_OK; +} + +/* Channel write, from 7909 channel program */ + +t_stat com_chwr (uint32 ch, t_uint64 val, uint32 stopf) +{ +if (stopf) + com_stop = 1; +else { + com_chob = val; /* store data */ + com_chob_v = 1; /* set valid */ + } +return SCPE_OK; +} + +/* Unit service - SNS */ + +t_stat coms_svc (UNIT *uptr) +{ +t_uint64 dat; + +switch (com_sta) { /* case on state */ + + case CHSL_SNS: /* prepare data */ + com_sns &= ~COMS_DYN; /* clear dynamic flags */ + if (com_free.head) /* free space? */ + com_set_sns (COMS_INBF); + if (com_inpq.head) /* pending input? */ + com_set_sns (COMS_DATR); + com_buf[0] = (com_sns >> 24) & DMASK; /* buffer is 2 words */ + com_buf[1] = (com_sns << 12) & DMASK; + com_bptr = 0; + com_blim = 2; + com_sta = CHSL_SNS|CHSL_2ND; /* 2nd state */ + break; + + case CHSL_SNS|CHSL_2ND: /* second state */ + if (com_bptr >= com_blim) { /* end of buffer? */ + ch9_set_end (com_ch, 0); /* set end */ + ch_req |= REQ_CH (com_ch); /* request channel */ + com_sta = CHSL_SNS|CHSL_3RD; /* 3rd state */ + sim_activate (uptr, 10 * uptr->wait); /* longer wait */ + return SCPE_OK; + } + dat = com_buf[com_bptr++]; /* get word */ + if (!com_stop) /* send wd to chan */ + ch9_req_rd (com_ch, dat); + break; + + case CHSL_SNS|CHSL_3RD: /* 3rd state */ + if (com_qdone (com_ch)) /* done? exit */ + return SCPE_OK; + com_sta = CHSL_SNS; /* repeat sequence */ + break; + } + +sim_activate (uptr, uptr->wait); /* sched next */ +return SCPE_OK; +} + +/* Unit service - channel program */ + +t_stat comc_svc (UNIT *uptr) +{ +uint32 i, j, k, ccnt, ln, uln, ent; +uint16 chr; +t_uint64 dat; + +switch (com_sta) { /* case on state */ + + case CHSL_RDS: /* read start */ + for (i = 0; i < COM_BUFSIZ; i++) /* clear chan buf */ + com_buf[i] = 0; + com_buf[0] = com_msgn; /* 1st char is msg num */ + com_msgn = (com_msgn + 1) & 03777; /* incr msg num */ + for (i = 1, j = 0; i < COMI_12BMAX; i++) { /* fill buffer */ + ent = com_gethd_free (&com_inpq); /* get next entry */ + if (ent == 0) /* q empty, done */ + break; + if ((i % 3) == 0) /* next word? */ + j++; + com_buf[j] = (com_buf[j] << 12) | /* pack data */ + ((t_uint64) (com_pkt[ent].data & 07777)); + } + for (k = i % 3; k < 3; k++) { /* fill with EOM */ + if (k == 0) /* next word? */ + j++; + com_buf[j] = (com_buf[j] << 12) | COMI_EOM; + } + com_bptr = 0; /* init buf ptr */ + com_blim = j + 1; /* save buf size */ + com_sta = CHSL_RDS|CHSL_2ND; /* next state */ + break; + + case CHSL_RDS|CHSL_2ND: /* read xmit word */ + if (com_bptr >= com_blim) /* transfer done? */ + com_end (com_ch, 0, CHSL_RDS|CHSL_3RD); /* end, next state */ + else { /* more to do */ + dat = com_buf[com_bptr++]; /* get word */ + if (!com_stop) /* give to channel */ + ch9_req_rd (com_ch, dat); + } + break; + + case CHSL_RDS|CHSL_3RD: /* read end */ + if (com_qdone (com_ch)) /* done? */ + return com_test_atn (com_ch); /* test atn, exit */ + com_sta = CHSL_RDS; /* repeat sequence */ + break; + + case CHSL_WRS: /* write start */ + for (i = 0; i < COM_BUFSIZ; i++) /* clear chan buf */ + com_buf[i] = 0; + com_bptr = 0; /* init buf ptr */ + com_sta = CHSL_WRS|CHSL_2ND; /* next state */ + ch_req |= REQ_CH (com_ch); /* request channel */ + com_chob = 0; /* clr, inval buf */ + com_chob_v = 0; + break; + + case CHSL_WRS|CHSL_2ND: /* write first word */ + dat = com_getob (com_ch); /* get word? */ + if (dat == 0777777777777) { /* turn on? */ + com_enab = 1; /* enable 7750 */ + com_msgn = 0; /* init message # */ + com_end (com_ch, 0, CHSL_WRS|CHSL_4TH); /* end, last state */ + } + else if (dat & COMO_LINCTL) { /* control message? */ + ln = COMO_GETLN (dat); /* line number */ + if (ln >= (COM_TLINES + COM_LBASE)) /* invalid line? */ + return STOP_INVLIN; + if (dat & COMO_CTLRST) /* char must be 0 */ + return STOP_INVMSG; + if (ln >= COM_LBASE) + com_reset_ln (ln - COM_LBASE); + com_end (com_ch, 0, CHSL_WRS|CHSL_4TH); /* end, last state */ + } + else { /* data message */ + ccnt = (((uint32) dat >> 12) & 07777) + 1; /* char count plus EOM */ + if (dat & COMO_LIN12B) /* 12b? double */ + ccnt = ccnt << 1; + com_blim = (ccnt + 6 + 5) / 6; /* buffer limit */ + if ((com_blim == 1) || (com_blim >= COMO_BMAX)) + return STOP_INVMSG; + com_buf[com_bptr++] = dat; /* store word */ + com_sta = CHSL_WRS|CHSL_3RD; /* next state */ + ch_req |= REQ_CH (com_ch); /* request channel */ + } + break; + + case CHSL_WRS|CHSL_3RD: /* other words */ + dat = com_getob (com_ch); /* get word */ + com_buf[com_bptr++] = dat; /* store word */ + if (com_bptr >= com_blim) { /* transfer done? */ + ln = COMO_GETLN (com_buf[0]); /* line number */ + if (ln >= (COM_TLINES + COM_LBASE)) /* invalid line? */ + return STOP_INVLIN; + if ((com_buf[0] & COMO_LIN12B) && /* 12b message? */ + (ln >= COM_LBASE)) { + uln = ln - COM_LBASE; /* unit number */ + for (i = 2, j = 0; i < COMO_12BMAX; i++) { /* unpack 12b char */ + if ((i % 3) == 0) + j++; + chr = (uint16) (com_buf[j] >> ((2 - (i % 3)) * 12)) & 07777; + if (chr == COMO_EOM12B) /* EOM? */ + break; + if (!com_new_puttl (&com_outq[uln], chr)) + return STOP_NOOFREE; /* append to outq */ + } + sim_activate (&coml_unit[uln], coml_unit[uln].wait); + } + com_end (com_ch, 0, CHSL_WRS|CHSL_4TH); /* end, last state */ + } + else if (!com_stop) /* request channel */ + ch_req |= REQ_CH (com_ch); + break; + + case CHSL_WRS|CHSL_4TH: /* buffer done */ + if (com_qdone (com_ch)) /* done? */ + return com_test_atn (com_ch); /* test atn, exit */ + com_sta = CHSL_WRS; /* repeat sequence */ + break; + + default: + return SCPE_IERR; + } + +sim_activate (uptr, uptr->wait); +return SCPE_OK; +} + +/* Unit service - console receive - always running, even if device is not */ + +t_stat comti_svc (UNIT *uptr) +{ +int32 c; +t_stat r; + +sim_activate (uptr, uptr->wait); /* continue poll */ +c = sim_poll_kbd (); /* get character */ +if (c && !(c & (SCPE_BREAK|SCPE_KFLAG))) /* error? */ + return c; +if (!com_enab || (c & SCPE_BREAK)) /* !enab, break? done */ + return SCPE_OK; +if (coml_unit[COM_MLINES].NEEDID) /* ID needed? */ + return com_send_id (COM_MLINES); +if ((c & SCPE_KFLAG) && ((c = c & 0177) != 0)) { /* char input? */ + if (r = com_queue_in (COM_MLINES, c)) + return r; + if (sim_tt_outcvt (c, TT_MODE_7P) >= 0) + sim_putchar (c); + if (c == '\r') + sim_putchar ('\n'); + } +return com_test_atn (com_ch); /* set ATN if input */ +} + +/* Unit service - receive side + + Poll all active lines for input + Poll for new connections */ + +t_stat comi_svc (UNIT *uptr) +{ +int32 c, ln, t; +t_stat r; + +if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return SCPE_OK; +t = sim_rtcn_calb (com_tps, TMR_COM); /* calibrate */ +sim_activate (uptr, t); /* continue poll */ +if (!com_enab) /* not enabled? exit */ + return SCPE_OK; +ln = tmxr_poll_conn (&com_desc); /* look for connect */ +if (ln >= 0) { /* got one? */ + com_ldsc[ln].rcve = 1; /* rcv enabled */ + coml_unit[ln].CONN = 1; /* flag connected */ + coml_unit[ln].NEEDID = 1; /* need ID */ + } +tmxr_poll_rx (&com_desc); /* poll for input */ +for (ln = 0; ln < COM_MLINES; ln++) { /* loop thru mux */ + if (com_ldsc[ln].conn) { /* connected? */ + if (coml_unit[ln].NEEDID) + return com_send_id (ln); + c = tmxr_getc_ln (&com_ldsc[ln]); /* get char */ + if (c) { /* any char? */ + c = c & 0177; /* mask to 7b */ + if (r = com_queue_in (ln, c)) /* queue char, err? */ + return r; + if (com_ldsc[ln].xmte) { /* output enabled? */ + if (sim_tt_outcvt (c, TT_MODE_7P) >= 0) /* echo char */ + tmxr_putc_ln (&com_ldsc[ln], c); + if (c == '\r') /* add LF after CR */ + tmxr_putc_ln (&com_ldsc[ln], '\n'); + tmxr_poll_tx (&com_desc); /* poll xmt */ + } /* end if enabled */ + } /* end if char */ + } /* end if conn */ + else if (coml_unit[ln].CONN) { /* not conn, was conn? */ + coml_unit[ln].CONN = 0; /* clear connected */ + coml_unit[ln].NEEDID = 0; /* clear need id */ + if (!com_inp_msg (ln, COMI_HANGUP)) /* hangup message */ + return STOP_NOIFREE; + } + } /* end for */ +return com_test_atn (com_ch); /* set ATN if input */ +} + +/* Unit service - console transmit */ + +t_stat comto_svc (UNIT *uptr) +{ +uint32 c, c1; + +if (com_outq[COM_MLINES].head == 0) /* no more characters? */ + return com_send_ccmp (COM_MLINES); /* free any remaining */ +c = com_queue_out (COM_MLINES, &c1); /* get character, cvt */ +if (c) /* printable? output */ + sim_putchar (c); +if (c1) /* second char? output */ + sim_putchar (c1); +sim_activate (uptr, uptr->wait); /* next char */ +if (com_not_ret[COM_MLINES] >= COMI_CMAX) /* completion needed? */ + return com_send_ccmp (COM_MLINES); /* generate msg */ +return SCPE_OK; +} + +/* Unit service - transmit side */ + +t_stat como_svc (UNIT *uptr) +{ +uint32 c, c1; +int32 ln = uptr - coml_unit; /* line # */ + +if (com_outq[ln].head == 0) /* no more characters? */ + return com_send_ccmp (ln); /* free any remaining */ +if (com_ldsc[ln].conn) { /* connected? */ + if (com_ldsc[ln].xmte) { /* output enabled? */ + c = com_queue_out (ln, &c1); /* get character, cvt */ + if (c) /* printable? output */ + tmxr_putc_ln (&com_ldsc[ln], c); + if (c1) /* print second */ + tmxr_putc_ln (&com_ldsc[ln], c1); + } /* end if */ + tmxr_poll_tx (&com_desc); /* poll xmt */ + sim_activate (uptr, uptr->wait); /* next char */ + if (com_not_ret[ln] >= COMI_CMAX) /* completion needed? */ + return com_send_ccmp (ln); /* generate msg */ + } /* end if conn */ +return SCPE_OK; +} + +/* Send ID sequence on input */ + +t_stat com_send_id (uint32 ln) +{ +com_inp_msg (ln, COMI_DIALUP); /* input message: */ +if (coml_unit[ln].flags & UNIT_K35) /* dialup, ID, endID */ + com_inp_msg (ln, COMI_K35); +else com_inp_msg (ln, COMI_K37); +com_inp_msg (ln, 0); +com_inp_msg (ln, 0); +com_inp_msg (ln, 0); +com_inp_msg (ln, 0); +com_inp_msg (ln, (uint16) (ln + COM_LBASE)); +if (!com_inp_msg (ln, COMI_ENDID)) /* make sure there */ + return STOP_NOIFREE; /* was room for msg */ +coml_unit[ln].NEEDID = 0; +return SCPE_OK; +} + +/* Translate and queue input character */ + +t_stat com_queue_in (uint32 ln, uint32 c) +{ +uint16 out; + +if (c == com_intr) + out = COMI_INTR; +else if (c == com_quit) + out = COMI_QUIT; +else { + if (coml_unit[ln].flags & UNIT_K35) { /* KSR-35? */ + if (islower (c)) /* convert LC to UC */ + c = toupper (c); + } + else c |= (com_epar[c]? COMI_PARITY: 0); /* add even parity */ + out = (~c) & 0377; /* 1's complement */ + } +if (!com_inp_msg (ln, out)) /* input message */ + return STOP_NOIFREE; +return SCPE_OK; +} + +/* Retrieve and translate output character */ + +uint32 com_queue_out (uint32 ln, uint32 *c1) +{ +uint32 c, ent, raw; + +*c1 = 0; /* assume non-printing */ +ent = com_gethd_free (&com_outq[ln]); /* get character */ +if (ent == 0) /* nothing? */ + return 0; +raw = com_pkt[ent].data; /* get 12b character */ +com_not_ret[ln]++; +if (raw == COMO_BITRPT) { /* insert delay? */ + com_skip_outc (ln); + return 0; + } +c = (~raw >> 1) & 0177; /* remove start, parity */ +if (c >= 040) { /* printable? */ + if (c == 0177) /* DEL? ignore */ + return 0; + if ((coml_unit[ln].flags & UNIT_K35) && islower (c)) /* KSR-35 LC? */ + c = toupper (c); /* cvt to UC */ + return c; + } +switch (c) { + + case '\t': case '\f': case '\b': case '\a': /* valid ctrls */ + return c; + + case '\r': /* carriage return? */ + if (coml_unit[ln].flags & UNIT_K35) /* KSR-35? */ + *c1 = '\n'; /* lf after cr */ + return c; + + case '\n': /* line feed? */ + if (!(coml_unit[ln].flags & UNIT_K35)) { /* KSR-37? */ + *c1 = '\n'; /* lf after cr */ + return '\r'; + } + return c; /* print lf */ + + case 022: /* DC2 */ + if (!(com_unit[ln].flags & UNIT_K35)) { /* KSR-37? */ + com_skip_outc (ln); /* skip next */ + return '\n'; /* print lf */ + } + break; + + case 023: /* DC3 */ + if (!(com_unit[ln].flags & UNIT_K35)) /* KSR-37? */ + com_skip_outc (ln); /* skip next */ + break; + } + +return 0; /* ignore others */ +} + +/* Generate completion message, if needed */ + +t_stat com_send_ccmp (uint32 ln) +{ +uint32 t; + +if (t = com_not_ret[ln]) { /* chars not returned? */ + if (t > COMI_CMAX) /* limit to max */ + t = COMI_CMAX; + com_not_ret[ln] -= t; /* keep count */ + if (!com_inp_msg (ln, COMI_COMP (t))) /* gen completion msg */ + return STOP_NOIFREE; + } +return SCPE_OK; +} + +/* Skip next char in output queue */ + +void com_skip_outc (uint32 ln) +{ +if (com_gethd_free (&com_outq[ln])) /* count it */ + com_not_ret[ln]++; +return; +} + +/* Read and validate output buffer */ + +t_uint64 com_getob (uint32 ch) +{ +if (com_chob_v) /* valid? clear */ + com_chob_v = 0; +else if (!com_stop) { /* not stopped? */ + ch9_set_ioc (com_ch); /* IO check */ + com_set_sns (COMS_ITMO); /* set sense bit */ + } +return com_chob; +} + +/* Set attention if input pending */ + +t_stat com_test_atn (uint32 ch) +{ +if (com_inpq.head) + ch9_set_atn (ch); +return SCPE_OK; +} + +/* Test for done */ + +t_bool com_qdone (uint32 ch) +{ +if (com_stop || !ch9_qconn (ch)) { /* stop or err disc? */ + com_sta = 0; /* ctrl is idle */ + return TRUE; + } +return FALSE; +} + +/* Channel end */ + +void com_end (uint32 ch, uint32 fl, uint32 st) +{ +ch9_set_end (ch, fl); /* set end */ +ch_req |= REQ_CH (ch); +com_sta = st; /* next state */ +return; +} + +/* List routines - remove from head and free */ + +uint16 com_gethd_free (LISTHD *lh) +{ +uint16 ent; + +if ((ent = com_gethd (lh)) != 0) + com_puttl (&com_free, ent); +return ent; +} + +/* Get free entry and insert at tail */ + +t_bool com_new_puttl (LISTHD *lh, uint16 val) +{ +uint16 ent; + +if ((ent = com_gethd (&com_free)) != 0) { + com_pkt[ent].data = val; + com_puttl (lh, ent); + return TRUE; + } +return FALSE; +} + +/* Remove from head */ + +uint16 com_gethd (LISTHD *lh) +{ +uint16 ent; + +if ((ent = lh->head) != 0) { + lh->head = com_pkt[ent].next; + if (lh->head == 0) + lh->tail = 0; + } +else lh->tail = 0; +return ent; +} + +/* Insert at tail */ + +void com_puttl (LISTHD *lh, uint16 ent) +{ +if (lh->tail == 0) + lh->head = ent; +else com_pkt[lh->tail].next = ent; +com_pkt[ent].next = 0; +lh->tail = ent; +return; +} + +/* Insert line and message into input queue */ + +t_bool com_inp_msg (uint32 ln, uint16 msg) +{ +uint16 ent1, ent2; + +if ((ent1 = com_gethd (&com_free)) != 0) { /* pkt avail? */ + if ((ent2 = com_gethd (&com_free)) != 0) { /* 2nd pkt avail? */ + com_pkt[ent1].data = (ln + COM_LBASE) | COMI_VALIDL; /* 1st pkt = line# */ + com_pkt[ent2].data = msg; /* 2nd pkt = char */ + com_puttl (&com_inpq, ent1); /* queue pkts */ + com_puttl (&com_inpq, ent2); + return TRUE; + } + com_puttl (&com_free, ent1); /* free 1st */ + } +return FALSE; /* failed */ +} + +/* Set flag in sense */ + +void com_set_sns (t_uint64 stat) +{ +com_sns |= stat; +com_sns &= ~(COMS_PCHK|COMS_DCHK|COMS_EXCC); +if (com_sns & COMS_PALL) + com_sns |= COMS_PCHK; +if (com_sns & COMS_DALL) + com_sns |= COMS_DCHK; +if (com_sns & COMS_EALL) + com_sns |= COMS_EXCC; +return; +} + +/* Reset routine */ + +t_stat com_reset (DEVICE *dptr) +{ +uint32 i; + +if (dptr->flags & DEV_DIS) { /* disabled? */ + com_dev.flags = com_dev.flags | DEV_DIS; /* disable lines */ + coml_dev.flags = coml_dev.flags | DEV_DIS; + } +else { + com_dev.flags = com_dev.flags & ~DEV_DIS; /* enable lines */ + coml_dev.flags = coml_dev.flags & ~DEV_DIS; + } +sim_activate (&com_unit[COM_CIU], com_unit[COM_CIU].wait); /* console */ +sim_cancel (&com_unit[COM_PLU]); +sim_cancel (&com_unit[COM_CHU]); +if (com_unit[COM_PLU].flags & UNIT_ATT) { /* master att? */ + int32 t = sim_rtcn_init (com_unit[COM_PLU].wait, TMR_COM); + sim_activate (&com_unit[COM_PLU], t); + } +com_enab = 0; +com_sns = 0; +com_msgn = 0; +com_sta = 0; +com_chob = 0; +com_chob_v = 0; +com_stop = 0; +com_bptr = 0; +com_blim = 0; +for (i = 0; i < COM_BUFSIZ; i++) + com_buf[i] = 0; +com_inpq.head = 0; /* init queues */ +com_inpq.tail = 0; +for (i = 0; i < COM_TLINES; i++) { + com_outq[i].head = 0; + com_outq[i].tail = 0; + com_reset_ln (i); + } +com_pkt[0].next = 0; /* init free list */ +for (i = 1; i < COM_PKTSIZ; i++) { + com_pkt[i].next = i + 1; + com_pkt[i].data = 0; + } +com_pkt[COM_PKTSIZ - 1].next = 0; /* end of free list */ +com_free.head = 1; +com_free.tail = COM_PKTSIZ - 1; +coml_unit[COM_MLINES].CONN = 1; /* console always conn */ +coml_unit[COM_MLINES].NEEDID = 1; +return SCPE_OK; +} + +/* Attach master unit */ + +t_stat com_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = tmxr_attach (&com_desc, uptr, cptr); /* attach */ +if (r != SCPE_OK) /* error */ + return r; +sim_rtcn_init (uptr->wait, TMR_COM); +sim_activate (uptr, 100); /* quick poll */ +return SCPE_OK; +} + +/* Detach master unit */ + +t_stat com_detach (UNIT *uptr) +{ +uint32 i; +t_stat r; + +r = tmxr_detach (&com_desc, uptr); /* detach */ +for (i = 0; i < COM_MLINES; i++) /* disable rcv */ + com_ldsc[i].rcve = 0; +sim_cancel (uptr); /* stop poll */ +return r; +} + +/* Reset an individual line */ + +void com_reset_ln (uint32 ln) +{ +while (com_gethd_free (&com_outq[ln])) ; +com_not_ret[ln] = 0; +sim_cancel (&coml_unit[ln]); +if ((ln < COM_MLINES) && (com_ldsc[ln].conn == 0)) + coml_unit[ln].CONN = 0; +return; +} + +/* Special show commands */ + +uint32 com_show_qsumm (FILE *st, LISTHD *lh, char *name) +{ +uint32 i, next; + +next = lh->head; +for (i = 0; i < COM_PKTSIZ; i++) { + if (next == 0) { + if (i == 0) + fprintf (st, "%s is empty\n", name); + else if (i == 1) + fprintf (st, "%s has 1 entry\n", name); + else fprintf (st, "%s had %d entries\n", name, i); + return i; + } + next = com_pkt[next].next; + } +fprintf (st, "%s is corrupt\n", name); +return 0; +} + +void com_show_char (FILE *st, uint32 ch) +{ +uint32 c; + +fprintf (st, "%03o", ch); +c = (~ch) & 0177; +if (((ch & 07400) == 0) && (c >= 040) && (c != 0177)) + fprintf (st, "[%c]", c); +return; +} + +t_stat com_show_freeq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +com_show_qsumm (st, &com_free, "Free queue"); +return SCPE_OK; +} + +t_stat com_show_inq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 entc, ln, i, next; + +if (entc = com_show_qsumm (st, &com_inpq, "Input queue")) { + for (i = 0, next = com_inpq.head; next != 0; + i++, next = com_pkt[next].next) { + if ((i % 4) == 0) + fprintf (st, "%d:\t", i); + ln = com_pkt[next].data; + next = com_pkt[next].next; + if (next == 0) { + fprintf (st, "Line number without data\n"); + return SCPE_OK; + } + fprintf (st, "%d/", ln); + com_show_char (st, com_pkt[next].data); + fputc ((((i % 4) == 3)? '\n': '\t'), st); + } + if (i % 4) + fputc ('\n', st); + } +return SCPE_OK; +} + +t_stat com_show_outq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 entc, ln, i, next; +char name[20]; + +ln = uptr - com_dev.units; +sprintf (name, "Output queue %d", ln); +if (entc = com_show_qsumm (st, &com_outq[ln], name)) { + for (i = 0, next = com_outq[ln].head; next != 0; + i++, next = com_pkt[next].next) { + if ((i % 8) == 0) + fprintf (st, "%d:\t", i); + com_show_char (st, com_pkt[next].data >> 1); + fputc ((((i % 8) == 7)? '\n': '\t'), st); + } + if (i % 8) + fputc ('\n', st); + } +return SCPE_OK; +} + +t_stat com_show_aoutq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 i; + +for (i = 0; i < COM_TLINES; i++) + com_show_outq (st, com_dev.units + i, 1, desc); +return SCPE_OK; +} + +t_stat com_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (!com_enab) + fprintf (st, "Controller is not initialized\n"); +if (val & COMR_FQ) + com_show_freeq (st, uptr, 1, desc); +if (val & COMR_IQ) + com_show_inq (st, uptr, 1, desc); +if (val & COMR_OQ) + com_show_aoutq (st, uptr, 1, desc); +return SCPE_OK; +} diff --git a/I7094/i7094_cpu.c b/I7094/i7094_cpu.c index 87c46fca..2ea5d8fe 100644 --- a/I7094/i7094_cpu.c +++ b/I7094/i7094_cpu.c @@ -1,6 +1,6 @@ /* i7094_cpu.c: IBM 7094 CPU simulator - Copyright (c) 2003-2007, Robert M. Supnik + Copyright (c) 2003-2010, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,8 @@ cpu 7094 central processor + 16-Jul-10 RMS Fixed PSE, MSE user mode protection (found by Dave Pitts) + Fixed issues in storage nullification mode 28-Apr-07 RMS Removed clock initialization 29-Oct-06 RMS Added additional expanded core instructions 17-Oct-06 RMS Fixed the fix in halt IO wait loop @@ -148,6 +150,8 @@ #define HALT_IO_LIMIT ((2 << 18) + 1) /* max wait to stop */ +#define EAMASK (mode_storn? A704_MASK: AMASK) /* eff addr mask */ + t_uint64 *M = NULL; /* memory */ t_uint64 AC = 0; /* AC */ t_uint64 MQ = 0; /* MQ */ @@ -185,7 +189,6 @@ uint32 ht_pend = 0; /* HTR pending */ uint32 ht_addr = 0; /* HTR address */ uint32 stop_illop = 1; /* stop on ill op */ uint32 cpu_astop = 0; /* address stop */ -static uint32 eamask = AMASK; /* (dynamic) addr mask */ uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ int32 pcq_p = 0; /* PC queue ptr */ @@ -619,7 +622,6 @@ t_bool tracing; ch_set_map (); /* set dispatch map */ if (!(cpu_model & (I_94|I_CT))) /* ~7094? MTM always on */ mode_multi = 1; -eamask = mode_storn? A704_MASK: AMASK; /* set eff addr mask */ inst_base = inst_base & ~AMASK; /* A/B sel is 1b */ data_base = data_base & ~AMASK; ind_reloc = ind_reloc & VA_BLK; /* canonical form */ @@ -684,7 +686,7 @@ while (reason == SCPE_OK) { /* loop until error */ chtr_pend = chtr_eval (NULL); /* re-evaluate */ } oldPC = PC; /* save current PC */ - PC = (PC + 1) & eamask; /* increment PC */ + PC = (PC + 1) & EAMASK; /* increment PC */ if (!ReadI (oldPC, &IR)) /* get inst; trap? */ continue; } @@ -695,7 +697,7 @@ while (reason == SCPE_OK) { /* loop until error */ XEC: tag = GET_TAG (IR); /* get tag */ - addr = (uint32) IR & eamask; /* get base addr */ + addr = (uint32) IR & EAMASK; /* get base addr */ /* Decrement format instructions */ @@ -788,15 +790,15 @@ while (reason == SCPE_OK) { /* loop until error */ continue; } if (tag && (fl & I_X)) /* tag and indexable? */ - ea = (addr - get_xri (tag)) & eamask; /* do indexing */ + ea = (addr - get_xri (tag)) & EAMASK; /* do indexing */ else ea = addr; if (TST_IND (IR) && (fl & I_N)) { /* indirect? */ if (!ReadI (ea, &SR)) /* get ind; trap? */ continue; - addr = (uint32) SR & eamask; /* get address */ + addr = (uint32) SR & EAMASK; /* get address */ tagi = GET_TAG (SR); /* get tag */ if (tagi) /* tag? */ - ea = (addr - get_xri (tagi)) & eamask; /* do indexing */ + ea = (addr - get_xri (tagi)) & EAMASK; /* do indexing */ else ea = addr; } if ((fl & I_R) && !Read (ea, &SR)) /* read opnd; trap? */ @@ -901,7 +903,7 @@ while (reason == SCPE_OK) { /* loop until error */ case 00054: /* RFT */ t = IR & RMASK; if ((SI & t) == 0) /* if ind off, skip */ - PC = (PC + 1) & eamask; + PC = (PC + 1) & EAMASK; break; case 00055: /* SIR */ @@ -911,7 +913,7 @@ while (reason == SCPE_OK) { /* loop until error */ case 00056: /* RNT */ t = IR & RMASK; if ((SI & t) == t) /* if ind on, skip */ - PC = (PC + 1) & eamask; + PC = (PC + 1) & EAMASK; break; case 00057: /* RIR */ @@ -952,7 +954,7 @@ while (reason == SCPE_OK) { /* loop until error */ sc = GET_CCNT (IR); SR = ea; while (sc) { - ea = (uint32) ((AC & 077) + SR) & eamask; + ea = (uint32) ((AC & 077) + SR) & EAMASK; if (!Read (ea, &SR)) break; AC = (AC & AC_S) | ((AC >> 6) & 0017777777777) | @@ -1152,12 +1154,12 @@ while (reason == SCPE_OK) { /* loop until error */ t2 = SR & MMASK; if (s1 ^ s2) { /* diff signs? */ if (s1) /* AC < mem? skip 2 */ - PC = (PC + 2) & eamask; + PC = (PC + 2) & EAMASK; } else if (t1 == t2) /* equal? skip 1 */ - PC = (PC + 1) & eamask; + PC = (PC + 1) & EAMASK; else if ((t1 < t2) ^ s1) /* AC < mem, AC +, or */ - PC = (PC + 2) & eamask; /* AC > mem, AC -? */ + PC = (PC + 2) & EAMASK; /* AC > mem, AC -? */ break; case 00361: /* ACL */ @@ -1208,7 +1210,7 @@ while (reason == SCPE_OK) { /* loop until error */ case 00444: /* OFT */ if ((SI & SR) == 0) /* skip if ind off */ - PC = (PC + 1) & eamask; + PC = (PC + 1) & EAMASK; break; case 00445: /* RIS */ @@ -1217,7 +1219,7 @@ while (reason == SCPE_OK) { /* loop until error */ case 00446: /* ONT */ if ((SI & SR) == SR) /* skip if ind on */ - PC = (PC + 1) & eamask; + PC = (PC + 1) & EAMASK; break; case 00460: /* LDA (704) */ @@ -1234,7 +1236,7 @@ while (reason == SCPE_OK) { /* loop until error */ case 00520: /* ZET */ if ((SR & MMASK) == 0) /* skip if M 1-35 = 0 */ - PC = (PC + 1) & eamask; + PC = (PC + 1) & EAMASK; break; case 00522: /* XEC */ @@ -1348,8 +1350,6 @@ while (reason == SCPE_OK) { /* loop until error */ break; case 00760: /* PSE */ - if (prot_trap (0)) /* user mode? */ - break; reason = op_pse (ea); break; @@ -1400,7 +1400,7 @@ while (reason == SCPE_OK) { /* loop until error */ case 01054: /* LFT */ t = (IR & RMASK) << 18; if ((SI & t) == 0) /* if ind off, skip */ - PC = (PC + 1) & eamask; + PC = (PC + 1) & EAMASK; break; case 01055: /* SIL */ @@ -1410,7 +1410,7 @@ while (reason == SCPE_OK) { /* loop until error */ case 01056: /* LNT */ t = (IR & RMASK) << 18; if ((SI & t) == t) /* if ind on, skip */ - PC = (PC + 1) & eamask; + PC = (PC + 1) & EAMASK; break; case 01057: /* RIL */ @@ -1441,7 +1441,7 @@ while (reason == SCPE_OK) { /* loop until error */ sc = GET_CCNT (IR); SR = ea; while (sc) { - ea = (uint32) ((MQ >> 30) + SR) & eamask; + ea = (uint32) ((MQ >> 30) + SR) & EAMASK; if (!Read (ea, &SR)) break; MQ = ((MQ << 6) & DMASK) | (MQ >> 30); @@ -1485,7 +1485,7 @@ while (reason == SCPE_OK) { /* loop until error */ sc = GET_CCNT (IR); SR = ea; while (sc) { - ea = (uint32) ((MQ >> 30) + SR) & eamask; + ea = (uint32) ((MQ >> 30) + SR) & EAMASK; if (!Read (ea, &SR)) break; MQ = ((MQ << 6) & DMASK) | (SR >> 30); @@ -1587,9 +1587,9 @@ while (reason == SCPE_OK) { /* loop until error */ case 01340: /* LAS */ t = AC & AC_MMASK; /* AC Q,P,1-35 */ if (t < SR) - PC = (PC + 2) & eamask; + PC = (PC + 2) & EAMASK; else if (t == SR) - PC = (PC + 1) & eamask; + PC = (PC + 1) & EAMASK; break; case 01400: /* SBM */ @@ -1606,7 +1606,7 @@ while (reason == SCPE_OK) { /* loop until error */ case 01520: /* NZT */ if ((SR & MMASK) != 0) - PC = (PC + 1) & eamask; + PC = (PC + 1) & EAMASK; break; case 01534: /* LXD */ @@ -1639,7 +1639,7 @@ while (reason == SCPE_OK) { /* loop until error */ SR = (AC & MMASK) | ((AC & AC_S)? SIGN: 0); if (!Write (ea, SR)) break; - Write ((ea + 1) & eamask, MQ); + Write ((ea + 1) & EAMASK, MQ); break; case 01620: /* SLQ */ @@ -1687,8 +1687,6 @@ while (reason == SCPE_OK) { /* loop until error */ break; /* -xr -> AC decr */ case 01760: /* MSE */ - if (prot_trap (0)) /* user mode? */ - break; reason = op_mse (ea); break; @@ -1701,11 +1699,11 @@ while (reason == SCPE_OK) { /* loop until error */ data_base = BCORE_BASE; else if (ea == 043) { /* IFT? */ if (inst_base == 0) - PC = (PC + 1) & eamask; + PC = (PC + 1) & EAMASK; } else if (ea == 044) { /* EFT? */ if (data_base == 0) - PC = (PC + 1) & eamask; + PC = (PC + 1) & EAMASK; } else if (stop_illop) reason = STOP_ILLEG; @@ -1967,9 +1965,9 @@ if (tag) { r = r | XR[2]; if (tag & 4) r = r | XR[4]; - return r & eamask; + return r & EAMASK; } - return XR[tag] & eamask; + return XR[tag] & EAMASK; } return 0; } @@ -1994,9 +1992,9 @@ if (tag) { if (tag & 4) r = r | XR[4]; put_xr (tag, r); - return r & eamask; + return r & EAMASK; } - return XR[tag] & eamask; + return XR[tag] & EAMASK; } return 0; } @@ -2006,7 +2004,7 @@ return 0; void put_xr (uint32 tag, uint32 dat) { tag = tag & INST_M_TAG; -dat = dat & eamask; +dat = dat & EAMASK; if (tag) { if (mode_multi) { diff --git a/I7094/i7094_cpu1.c b/I7094/i7094_cpu1.c index 18afca55..c5c4ea12 100644 --- a/I7094/i7094_cpu1.c +++ b/I7094/i7094_cpu1.c @@ -1,6 +1,6 @@ /* i7094_cpu1.c: IBM 7094 CPU complex instructions - Copyright (c) 2003-2008, Robert M. Supnik + Copyright (c) 2003-2010, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -22,6 +22,9 @@ Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + + 16-Jul-10 RMS Fixed PSE and MSE user-mode protection (from Dave Pitts) + Added SPUx, SPTx, SPRx */ #include "i7094_defs.h" @@ -44,6 +47,8 @@ extern uint32 mode_storn, mode_multi; extern uint32 chtr_pend, chtr_inht, chtr_inhi; extern uint32 ch_flags[NUM_CHAN]; +extern t_bool prot_trap (uint32 decr); + typedef struct { /* unpacked fp */ uint32 s; /* sign: 0 +, 1 - */ int32 ch; /* exponent */ @@ -279,6 +284,8 @@ switch (addr) { break; case 00005: /* IOT */ + if (prot_trap (0)) /* user mode? */ + break; if (ind_ioc) ind_ioc = 0; else PC = (PC + 1) & AMASK; @@ -289,6 +296,8 @@ switch (addr) { break; case 00007: /* ETM */ + if (prot_trap (0)) /* user mode? */ + break; if (cpu_model & I_9X) /* 709X only */ mode_ttrap = 1; break; @@ -324,7 +333,7 @@ switch (addr) { break; case 00140: /* SLF */ - if (cpu_model & I_9X) /* 709X only */ + if (cpu_model & I_9X) /* 709X only */ SLT = 0; break; @@ -339,10 +348,12 @@ switch (addr) { PC = (PC + 1) & AMASK; break; - case 01000: case 02000: case 03000: case 04000: /* BTT */ - case 05000: case 06000: case 07000: case 10000: - if (cpu_model & I_9X) { /* 709X only */ - if (sel_trap (PC)) /* sel trap? */ + case 001000: case 002000: case 003000: case 004000: /* BTT */ + case 005000: case 060000: case 070000: case 010000: + if (prot_trap (0)) /* user mode? */ + break; + if (cpu_model & I_9X) { /* 709X only */ + if (sel_trap (PC)) /* sel trap? */ break; ch = GET_U_CH (addr); /* get channel */ if (ch_flags[ch] & CHF_BOT) /* BOT? */ @@ -353,13 +364,41 @@ switch (addr) { case 001350: case 002350: case 003350: case 004350: /* RICx */ case 005350: case 006350: case 007350: case 010350: + if (prot_trap (0)) /* user mode? */ + break; ch = GET_U_CH (addr); /* get channel */ return ch_op_reset (ch, 1); case 001352: case 002352: case 003352: case 004352: /* RDCx */ case 005352: case 006352: case 007352: case 010352: + if (prot_trap (0)) /* user mode? */ + break; ch = GET_U_CH (addr); /* get channel */ return ch_op_reset (ch, 0); + + case 001341: case 002341: case 003341: case 004341: /* SPUx */ + case 005341: case 006341: case 007341: case 010341: + case 001342: case 002342: case 003342: case 004342: /* SPUx 2 */ + case 005342: case 006342: case 007342: case 010342: + case 001360: case 002360: case 003360: case 004360: /* SPTx */ + case 005360: case 006360: case 007360: case 010360: + case 001361: case 002361: case 003361: case 004361: /* SPRx */ + case 005361: case 006361: case 007361: case 010361: + case 001362: case 002362: case 003362: case 004362: /* SPRx 2 */ + case 005362: case 006362: case 007362: case 010362: + case 001363: case 002363: case 003363: case 004363: /* SPRx 3 */ + case 005363: case 006363: case 007363: case 010363: + case 001364: case 002364: case 003364: case 004364: /* SPRx 4 */ + case 005364: case 006364: case 007364: case 010364: + case 001365: case 002365: case 003365: case 004365: /* SPRx 5 */ + case 005365: case 006365: case 007365: case 010365: + case 001366: case 002366: case 003366: case 004366: /* SPRx 6 */ + case 005366: case 006366: case 007366: case 010366: + case 001367: case 002367: case 003367: case 004367: /* SPRx 7 */ + case 005367: case 006367: case 007367: case 010367: + if (prot_trap (0)) /* user mode? */ + break; + break; /* no ops */ } /* end case */ return SCPE_OK; @@ -384,6 +423,8 @@ switch (addr) { break; case 00002: /* EFTM */ + if (prot_trap (0)) /* user mode? */ + break; if (cpu_model & I_9X) { /* 709X only */ mode_ftrap = 1; ind_mqo = 0; /* clears MQ ovf */ @@ -396,6 +437,8 @@ switch (addr) { break; case 00004: /* LFTM */ + if (prot_trap (0)) /* user mode? */ + break; if (cpu_model & I_9X) /* 709X only */ mode_ftrap = 0; break; @@ -411,6 +454,8 @@ switch (addr) { break; case 00007: /* LTM */ + if (prot_trap (0)) /* user mode? */ + break; if (cpu_model & I_9X) /* 709X only */ mode_ttrap = 0; break; diff --git a/I7094/i7094_cpu1_old.c b/I7094/i7094_cpu1_old.c new file mode 100644 index 00000000..18afca55 --- /dev/null +++ b/I7094/i7094_cpu1_old.c @@ -0,0 +1,887 @@ +/* i7094_cpu1.c: IBM 7094 CPU complex instructions + + Copyright (c) 2003-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include "i7094_defs.h" + +#define FP_HIFRAC(x) ((uint32) ((x) >> FP_N_FR) & FP_FMASK) +#define FP_LOFRAC(x) ((uint32) (x) & FP_FMASK) + +#define FP_PACK38(s,e,f) (((s)? AC_S: 0) | ((t_uint64) (f)) | \ + (((t_uint64) ((e) & FP_M_ACCH)) << FP_V_CH)) +#define FP_PACK36(s,e,f) (((s)? SIGN: 0) | ((t_uint64) (f)) | \ + (((t_uint64) ((e) & FP_M_CH)) << FP_V_CH)) + +extern t_uint64 AC, MQ, SI, KEYS; +extern uint32 PC; +extern uint32 SLT, SSW; +extern uint32 cpu_model, stop_illop; +extern uint32 ind_ovf, ind_dvc, ind_ioc, ind_mqo; +extern uint32 mode_ttrap, mode_strap, mode_ctrap, mode_ftrap; +extern uint32 mode_storn, mode_multi; +extern uint32 chtr_pend, chtr_inht, chtr_inhi; +extern uint32 ch_flags[NUM_CHAN]; + +typedef struct { /* unpacked fp */ + uint32 s; /* sign: 0 +, 1 - */ + int32 ch; /* exponent */ + t_uint64 fr; /* fraction (54b) */ + } UFP; + +uint32 op_frnd (void); +t_uint64 fp_fracdiv (t_uint64 dvd, t_uint64 dvr, t_uint64 *rem); +void fp_norm (UFP *op); +void fp_unpack (t_uint64 h, t_uint64 l, t_bool q_ac, UFP *op); +uint32 fp_pack (UFP *op, uint32 mqs, int32 mqch); + +extern t_bool fp_trap (uint32 spill); +extern t_bool sel_trap (uint32 va); +extern t_stat ch_op_reset (uint32 ch, t_bool ch7909); + +/* Integer add + + Sherman: "As the result of an addition or subtraction, if the C(AC) is + zero, the sign of AC is unchanged." */ + +void op_add (t_uint64 op) +{ +t_uint64 mac = AC & AC_MMASK; /* get magnitudes */ +t_uint64 mop = op & MMASK; + +AC = AC & AC_S; /* isolate AC sign */ +if ((AC? 1: 0) ^ ((op & SIGN)? 1: 0)) { /* signs diff? sub */ + if (mac >= mop) /* AC >= MQ */ + AC = AC | (mac - mop); + else AC = (AC ^ AC_S) | (mop - mac); /* <, sign change */ + } +else { + AC = AC | ((mac + mop) & AC_MMASK); /* signs same, add */ + if ((AC ^ mac) & AC_P) /* P change? overflow */ + ind_ovf = 1; + } +return; +} + +/* Multiply */ + +void op_mpy (t_uint64 ac, t_uint64 sr, uint32 sc) +{ +uint32 sign; + +if (sc == 0) /* sc = 0? nop */ + return; +sign = ((MQ & SIGN)? 1: 0) ^ ((sr & SIGN)? 1: 0); /* result sign */ +ac = ac & AC_MMASK; /* clear AC sign */ +sr = sr & MMASK; /* mpy magnitude */ +MQ = MQ & MMASK; /* MQ magnitude */ +if (sr && MQ) { /* mpy != 0? */ + while (sc--) { /* for sc */ + if (MQ & 1) /* MQ35? AC += mpy */ + ac = (ac + sr) & AC_MMASK; + MQ = (MQ >> 1) | ((ac & 1) << 34); /* AC'MQ >> 1 */ + ac = ac >> 1; + } + } +else ac = MQ = 0; /* result = 0 */ +if (sign) { /* negative? */ + ac = ac | AC_S; /* insert signs */ + MQ = MQ | SIGN; + } +AC = ac; /* update AC */ +return; +} + +/* Divide */ + +t_bool op_div (t_uint64 sr, uint32 sc) +{ +uint32 signa, signm; + +if (sc == 0) /* sc = 0? nop */ + return FALSE; +signa = (AC & AC_S)? 1: 0; /* get signs */ +signm = (sr & SIGN)? 1: 0; +sr = sr & MMASK; /* get dvr magn */ +if ((AC & AC_MMASK) >= sr) /* |AC| >= |sr|? */ + return TRUE; +AC = AC & AC_MMASK; /* AC, MQ magn */ +MQ = MQ & MMASK; +while (sc--) { /* for sc */ + AC = ((AC << 1) & AC_MMASK) | (MQ >> 34); /* AC'MQ << 1 */ + MQ = (MQ << 1) & MMASK; + if (AC >= sr) { /* AC >= dvr? */ + AC = AC - sr; /* AC -= dvr */ + MQ = MQ | 1; /* set quo bit */ + } + } +if (signa ^ signm) /* quo neg? */ + MQ = MQ | SIGN; +if (signa) /* rem neg? */ + AC = AC | AC_S; +return FALSE; /* div ok */ +} + +/* Shifts */ + +void op_als (uint32 addr) +{ +uint32 sc = addr & SCMASK; + +if ((sc >= 35)? /* shift >= 35? */ + ((AC & MMASK) != 0): /* test all bits for ovf */ + (((AC & MMASK) >> (35 - sc)) != 0)) /* test only 35-sc bits */ + ind_ovf = 1; +if (sc >= 37) /* sc >= 37? result 0 */ + AC = AC & AC_S; +else AC = (AC & AC_S) | ((AC << sc) & AC_MMASK); /* shift, save sign */ +return; +} + +void op_ars (uint32 addr) +{ +uint32 sc = addr & SCMASK; + +if (sc >= 37) /* sc >= 37? result 0 */ + AC = AC & AC_S; +else AC = (AC & AC_S) | ((AC & AC_MMASK) >> sc); /* shift, save sign */ +return; +} + +void op_lls (uint32 addr) +{ +uint32 sc; /* get sc */ + +AC = AC & AC_MMASK; /* clear AC sign */ +for (sc = addr & SCMASK; sc != 0; sc--) { /* for SC */ + AC = ((AC << 1) & AC_MMASK) | ((MQ >> 34) & 1); /* AC'MQ << 1 */ + MQ = (MQ & SIGN) | ((MQ << 1) & MMASK); /* preserve MQ sign */ + if (AC & AC_P) /* if P, overflow */ + ind_ovf = 1; + } +if (MQ & SIGN) /* set ACS from MQS */ + AC = AC | AC_S; +return; +} + +void op_lrs (uint32 addr) +{ +uint32 sc = addr & SCMASK; +t_uint64 mac; + +MQ = MQ & MMASK; /* get MQ magnitude */ +if (sc != 0) { + mac = AC & AC_MMASK; /* get AC magnitude, */ + AC = AC & AC_S; /* sign */ + if (sc < 35) { /* sc [1,34]? */ + MQ = ((MQ >> sc) | (mac << (35 - sc))) & MMASK; /* MQ has AC'MQ */ + AC = AC | (mac >> sc); /* AC has AC only */ + } + else if (sc < 37) { /* sc [35:36]? */ + MQ = (mac >> (sc - 35)) & MMASK; /* MQ has AC only */ + AC = AC | (mac >> sc); /* AC has */ + } + else if (sc < 72) /* sc [37:71]? */ + MQ = (mac >> (sc - 35)) & MMASK; /* MQ has AC only */ + else MQ = 0; /* >72? MQ = 0 */ + } +if (AC & AC_S) /* set MQS from ACS */ + MQ = MQ | SIGN; +return; +} + +void op_lgl (uint32 addr) +{ +uint32 sc; /* get sc */ + +for (sc = addr & SCMASK; sc != 0; sc--) { /* for SC */ + AC = (AC & AC_S) | ((AC << 1) & AC_MMASK) | /* AC'MQ << 1 */ + ((MQ >> 35) & 1); /* preserve AC sign */ + MQ = (MQ << 1) & DMASK; + if (AC & AC_P) /* if P, overflow */ + ind_ovf = 1; + } +return; +} + +void op_lgr (uint32 addr) +{ +uint32 sc = addr & SCMASK; +t_uint64 mac; + +if (sc != 0) { + mac = AC & AC_MMASK; /* get AC magnitude, */ + AC = AC & AC_S; /* sign */ + if (sc < 36) { /* sc [1,35]? */ + MQ = ((MQ >> sc) | (mac << (36 - sc))) & DMASK; /* MQ has AC'MQ */ + AC = AC | (mac >> sc); /* AC has AC only */ + } + else if (sc == 36) { /* sc [36]? */ + MQ = mac & DMASK; /* MQ = AC */ + AC = AC | (mac >> 36); /* AC = AC */ + } + else if (sc < 73) /* sc [37, 72]? */ + MQ = (mac >> (sc - 36)) & DMASK; /* MQ has AC only */ + else MQ = 0; /* >72, AC,MQ = 0 */ + } +return; +} + +/* Plus sense - undefined operations are NOPs */ + +t_stat op_pse (uint32 addr) +{ +uint32 ch, spill; + +switch (addr) { + + case 00000: /* CLM */ + if (cpu_model & I_9X) /* 709X only */ + AC = AC & AC_S; + break; + + case 00001: /* LBT */ + if ((AC & 1) != 0) + PC = (PC + 1) & AMASK; + break; + + case 00002: /* CHS */ + AC = AC ^ AC_S; + break; + + case 00003: /* SSP */ + AC = AC & ~AC_S; + break; + + case 00004: /* ENK */ + MQ = KEYS; + break; + + case 00005: /* IOT */ + if (ind_ioc) + ind_ioc = 0; + else PC = (PC + 1) & AMASK; + break; + + case 00006: /* COM */ + AC = AC ^ AC_MMASK; + break; + + case 00007: /* ETM */ + if (cpu_model & I_9X) /* 709X only */ + mode_ttrap = 1; + break; + + case 00010: /* RND */ + if ((cpu_model & I_9X) && (MQ & B1)) /* 709X only, MQ1 set? */ + op_add ((t_uint64) 1); /* incr AC */ + break; + + case 00011: /* FRN */ + if (cpu_model & I_9X) { /* 709X only */ + spill = op_frnd (); + if (spill) + fp_trap (spill); + } + break; + + case 00012: /* DCT */ + if (ind_dvc) + ind_dvc = 0; + else PC = (PC + 1) & AMASK; + break; + + case 00014: /* RCT */ + chtr_inhi = 1; /* 1 cycle delay */ + chtr_inht = 0; /* clr inhibit trap */ + chtr_pend = 0; /* no trap now */ + break; + + case 00016: /* LMTM */ + if (cpu_model & I_94) /* 709X only */ + mode_multi = 0; + break; + + case 00140: /* SLF */ + if (cpu_model & I_9X) /* 709X only */ + SLT = 0; + break; + + case 00141: case 00142: case 00143: case 00144: /* SLN */ + if (cpu_model & I_9X) /* 709X only */ + SLT = SLT | (1u << (00144 - addr)); + break; + + case 00161: case 00162: case 00163: /* SWT */ + case 00164: case 00165: case 00166: + if ((SSW & (1u << (00166 - addr))) != 0) + PC = (PC + 1) & AMASK; + break; + + case 01000: case 02000: case 03000: case 04000: /* BTT */ + case 05000: case 06000: case 07000: case 10000: + if (cpu_model & I_9X) { /* 709X only */ + if (sel_trap (PC)) /* sel trap? */ + break; + ch = GET_U_CH (addr); /* get channel */ + if (ch_flags[ch] & CHF_BOT) /* BOT? */ + ch_flags[ch] &= ~CHF_BOT; /* clear */ + else PC = (PC + 1) & AMASK; /* else skip */ + } + break; + + case 001350: case 002350: case 003350: case 004350: /* RICx */ + case 005350: case 006350: case 007350: case 010350: + ch = GET_U_CH (addr); /* get channel */ + return ch_op_reset (ch, 1); + + case 001352: case 002352: case 003352: case 004352: /* RDCx */ + case 005352: case 006352: case 007352: case 010352: + ch = GET_U_CH (addr); /* get channel */ + return ch_op_reset (ch, 0); + } /* end case */ + +return SCPE_OK; +} + +/* Minus sense */ + +t_stat op_mse (uint32 addr) +{ +uint32 t, ch; + +switch (addr) { + + case 00000: /* CLM */ + if (cpu_model & I_9X) /* 709X only */ + AC = AC & AC_S; + break; + + case 00001: /* PBT */ + if ((AC & AC_P) != 0) + PC = (PC + 1) & AMASK; + break; + + case 00002: /* EFTM */ + if (cpu_model & I_9X) { /* 709X only */ + mode_ftrap = 1; + ind_mqo = 0; /* clears MQ ovf */ + } + break; + + case 00003: /* SSM */ + if (cpu_model & I_9X) /* 709X only */ + AC = AC | AC_S; + break; + + case 00004: /* LFTM */ + if (cpu_model & I_9X) /* 709X only */ + mode_ftrap = 0; + break; + + case 00005: /* ESTM */ + if (cpu_model & I_9X) /* 709X only */ + mode_strap = 1; + break; + + case 00006: /* ECTM */ + if (cpu_model & I_9X) /* 709X only */ + mode_ctrap = 1; + break; + + case 00007: /* LTM */ + if (cpu_model & I_9X) /* 709X only */ + mode_ttrap = 0; + break; + + case 00010: /* LSNM */ + if (cpu_model & I_9X) /* 709X only */ + mode_storn = 0; + break; + + case 00012: /* RTT (704) */ + if (cpu_model & I_9X) /* 709X only */ + sel_trap (PC); + break; + + case 00016: /* EMTM */ + mode_multi = 1; + break; + + case 00140: /* SLF */ + if (cpu_model & I_9X) /* 709X only */ + SLT = 0; + break; + + case 00141: case 00142: case 00143: case 00144: /* SLT */ + if (cpu_model & I_9X) { /* 709X only */ + t = SLT & (1u << (00144 - addr)); + SLT = SLT & ~t; + if (t != 0) + PC = (PC + 1) & AMASK; + } + break; + + case 00161: case 00162: case 00163: /* SWT */ + case 00164: case 00165: case 00166: + if ((cpu_model & I_9X) && /* 709X only */ + ((SSW & (1u << (00166 - addr))) != 0)) + PC = (PC + 1) & AMASK; + break; + + case 001000: case 002000: case 003000: case 004000: /* ETT */ + case 005000: case 006000: case 007000: case 010000: + if (sel_trap (PC)) /* sel trap? */ + break; + ch = GET_U_CH (addr); /* get channel */ + if (ch_flags[ch] & CHF_EOT) /* EOT? */ + ch_flags[ch] = ch_flags[ch] & ~CHF_EOT; /* clear */ + else PC = (PC + 1) & AMASK; /* else skip */ + break; + } + +return SCPE_OK; +} + +/* Floating add + + Notes: + - AC enter into the initial exponent comparison. If either is set, + the numbers are always swapped. AC

gets OR'd into AC during the + swap, and AC are cleared afterwards + - The early end test is actually > 077 if AC <= SR and > 100 if + AC > SR. However, any shift >= 54 will produce a zero fraction, + so the difference can be ignored */ + +uint32 op_fad (t_uint64 sr, t_bool norm) +{ +UFP op1, op2, t; +int32 mqch, diff; + +MQ = 0; /* clear MQ */ +fp_unpack (AC, 0, 1, &op1); /* unpack AC */ +fp_unpack (sr, 0, 0, &op2); /* unpack sr */ +if (op1.ch > op2.ch) { /* AC exp > SR exp? */ + if (AC & AC_P) /* AC P or's with S */ + op1.s = 1; + t = op1; /* swap operands */ + op1 = op2; + op2 = t; + op2.ch = op2.ch & FP_M_CH; /* clear P,Q */ + } +diff = op2.ch - op1.ch; /* exp diff */ +if (diff) { /* any shift? */ + if ((diff < 0) || (diff > 077)) /* diff > 63? */ + op1.fr = 0; + else op1.fr = op1.fr >> diff; /* no, denormalize */ + } +if (op1.s ^ op2.s) { /* subtract? */ + if (op1.fr >= op2.fr) { /* op1 > op2? */ + op2.fr = op1.fr - op2.fr; /* op1 - op2 */ + op2.s = op1.s; /* op2 sign is result */ + } + else op2.fr = op2.fr - op1.fr; /* else op2 - op1 */ + } +else { + op2.fr = op2.fr + op1.fr; /* op2 + op1 */ + if (op2.fr & FP_FCRY) { /* carry? */ + op2.fr = op2.fr >> 1; /* renormalize */ + op2.ch++; /* incr exp */ + } + } +if (norm) { /* normalize? */ + if (op2.fr) { /* non-zero frac? */ + fp_norm (&op2); + mqch = op2.ch - FP_N_FR; + } + else op2.ch = mqch = 0; /* else true zero */ + } +else mqch = op2.ch - FP_N_FR; +return fp_pack (&op2, op2.s, mqch); /* pack AC, MQ */ +} + +/* Floating multiply */ + +uint32 op_fmp (t_uint64 sr, t_bool norm) +{ +UFP op1, op2; +int32 mqch; +uint32 f1h, f2h; + +fp_unpack (MQ, 0, 0, &op1); /* unpack MQ */ +fp_unpack (sr, 0, 0, &op2); /* unpack sr */ +op1.s = op1.s ^ op2.s; /* result sign */ +if ((op2.ch == 0) && (op2.fr == 0)) { /* sr a normal 0? */ + AC = op1.s? AC_S: 0; /* result is 0 */ + MQ = op1.s? SIGN: 0; + return 0; + } +f1h = FP_HIFRAC (op1.fr); /* get hi fracs */ +f2h = FP_HIFRAC (op2.fr); +op1.fr = ((t_uint64) f1h) * ((t_uint64) f2h); /* f1h * f2h */ +op1.ch = (op1.ch & FP_M_CH) + op2.ch - FP_BIAS; /* result exponent */ +if (norm) { /* normalize? */ + if (!(op1.fr & FP_FNORM)) { /* not normalized? */ + op1.fr = op1.fr << 1; /* shift frac left 1 */ + op1.ch--; /* decr exp */ + } + if (FP_HIFRAC (op1.fr)) /* hi result non-zero? */ + mqch = op1.ch - FP_N_FR; /* set MQ exp */ + else op1.ch = mqch = 0; /* clear AC, MQ exp */ + } +else mqch = op1.ch - FP_N_FR; /* set MQ exp */ +return fp_pack (&op1, op1.s, mqch); /* pack AC, MQ */ +} + +/* Floating divide */ + +uint32 op_fdv (t_uint64 sr) +{ +UFP op1, op2; +int32 mqch; +uint32 spill, quos; +t_uint64 rem; + +fp_unpack (AC, 0, 1, &op1); /* unpack AC */ +fp_unpack (sr, 0, 0, &op2); /* unpack sr */ +quos = op1.s ^ op2.s; /* quotient sign */ +if (op1.fr >= (2 * op2.fr)) { /* |AC| >= 2*|sr|? */ + MQ = quos? SIGN: 0; /* MQ = sign only */ + return TRAP_F_DVC; /* divide check */ + } +if (op1.fr == 0) { /* |AC| == 0? */ + MQ = quos? SIGN: 0; /* MQ = sign only */ + AC = 0; /* AC = +0 */ + return 0; /* done */ + } +op1.ch = op1.ch & FP_M_CH; /* remove AC */ +if (op1.fr >= op2.fr) { /* |AC| >= |sr|? */ + op1.fr = op1.fr >> 1; /* denorm AC */ + op1.ch++; + } +op1.fr = fp_fracdiv (op1.fr, op2.fr, &rem); /* fraction divide */ +op1.fr = op1.fr | (rem << FP_N_FR); /* rem'quo */ +mqch = op1.ch - op2.ch + FP_BIAS; /* quotient exp */ +op1.ch = op1.ch - FP_N_FR; /* remainder exp */ +spill = fp_pack (&op1, quos, mqch); /* pack up */ +return (spill? (spill | TRAP_F_SGL): 0); /* if spill, set SGL */ +} + +/* Double floating add + + Notes: + - AC enter into the initial exponent comparison. If either is set, + the numbers are always swapped. AC

gets OR'd into AC during the + swap, and AC are cleared afterwards + - For most cases, SI ends up with the high order part of the larger number + - The 'early end' cases (smaller number is shifted away) must be tracked + exactly for SI impacts. The early end cases are: + + (a) AC > SR, diff > 0100, and AC normalized + (b) AC <= SR, diff > 077, and SR normalized + + In case (a), SI is unchanged. In case (b), SI ends up with the SR sign + and characteristic but the MQ (!) fraction */ + +uint32 op_dfad (t_uint64 sr, t_uint64 sr1, t_bool norm) +{ +UFP op1, op2, t; +int32 mqch, diff; + +fp_unpack (AC, MQ, 1, &op1); /* unpack AC'MQ */ +fp_unpack (sr, sr1, 0, &op2); /* unpack sr'sr1 */ +if (op1.ch > op2.ch) { /* AC exp > SR exp? */ + if (((op1.ch - op2.ch) > 0100) && (AC & B9)) ; /* early out */ + else SI = FP_PACK36 (op1.s, op1.ch, FP_HIFRAC (op1.fr)); + if (AC & AC_P) /* AC P or's with S */ + op1.s = 1; + t = op1; /* swap operands */ + op1 = op2; + op2 = t; + op2.ch = op2.ch & FP_M_CH; /* clear P,Q */ + } +else { /* AC <= SR */ + if (((op2.ch - op1.ch) > 077) && (sr & B9)) /* early out */ + SI = FP_PACK36 (op2.s, op2.ch, FP_LOFRAC (MQ)); + else SI = FP_PACK36 (op2.s, op2.ch, FP_HIFRAC (op2.fr)); + } +diff = op2.ch - op1.ch; /* exp diff */ +if (diff) { /* any shift? */ + if ((diff < 0) || (diff > 077)) /* diff > 63? */ + op1.fr = 0; + else op1.fr = op1.fr >> diff; /* no, denormalize */ + } +if (op1.s ^ op2.s) { /* subtract? */ + if (op1.fr >= op2.fr) { /* op1 > op2? */ + op2.fr = op1.fr - op2.fr; /* op1 - op2 */ + op2.s = op1.s; /* op2 sign is result */ + } + else op2.fr = op2.fr - op1.fr; /* op2 - op1 */ + } +else { + op2.fr = op2.fr + op1.fr; /* op2 + op1 */ + if (op2.fr & FP_FCRY) { /* carry? */ + op2.fr = op2.fr >> 1; /* renormalize */ + op2.ch++; /* incr exp */ + } + } +if (norm) { /* normalize? */ + if (op2.fr) { /* non-zero frac? */ + fp_norm (&op2); + mqch = op2.ch - FP_N_FR; + } + else op2.ch = mqch = 0; /* else true zero */ + } +else mqch = op2.ch - FP_N_FR; +return fp_pack (&op2, op2.s, mqch); /* pack AC, MQ */ +} + +/* Double floating multiply + + Notes (notation is A+B' * C+D', where ' denotes 2^-27): + - The instruction returns 0 if A and C are both zero, because B*D is never + done as part of the algorithm + - For most cases, SI ends up with B*C, with a zero sign and exponent + - For the A+B' both zero 'early end' case SI ends up with A or C, + depending on whether the operation is normalized or not */ + +uint32 op_dfmp (t_uint64 sr, t_uint64 sr1, t_bool norm) +{ +UFP op1, op2; +int32 mqch; +uint32 f1h, f2h, f1l, f2l; +t_uint64 tx; + +fp_unpack (AC, MQ, 1, &op1); /* unpack AC'MQ */ +fp_unpack (sr, sr1, 0, &op2); /* unpack sr'sr1 */ +op1.s = op1.s ^ op2.s; /* result sign */ +f1h = FP_HIFRAC (op1.fr); /* A */ +f1l = FP_LOFRAC (op1.fr); /* B */ +f2h = FP_HIFRAC (op2.fr); /* C */ +f2l = FP_LOFRAC (op2.fr); /* D */ +if (((op1.ch == 0) && (op1.fr == 0)) || /* AC'MQ normal 0? */ + ((op2.ch == 0) && (op2.fr == 0)) || /* sr'sr1 normal 0? */ + ((f1h == 0) && (f2h == 0))) { /* both hi frac zero? */ + AC = op1.s? AC_S: 0; /* result is 0 */ + MQ = op1.s? SIGN: 0; + SI = sr; /* SI has C */ + return 0; + } +op1.ch = (op1.ch & FP_M_CH) + op2.ch - FP_BIAS; /* result exponent */ +if (op1.fr) { /* A'B != 0? */ + op1.fr = ((t_uint64) f1h) * ((t_uint64) f2h); /* A * C */ + tx = ((t_uint64) f1h) * ((t_uint64) f2l); /* A * D */ + op1.fr = op1.fr + (tx >> FP_N_FR); /* add in hi 27b */ + tx = ((t_uint64) f1l) * ((t_uint64) f2h); /* B * C */ + op1.fr = op1.fr + (tx >> FP_N_FR); /* add in hi 27b */ + SI = tx >> FP_N_FR; /* SI keeps B * C */ + } +else { + if (norm) /* early out */ + SI = sr; + else SI = FP_PACK36 (op2.s, op2.ch, 0); + } +if (norm) { /* normalize? */ + if (!(op1.fr & FP_FNORM)) { /* not normalized? */ + op1.fr = op1.fr << 1; /* shift frac left 1 */ + op1.ch--; /* decr exp */ + } + if (FP_HIFRAC (op1.fr)) { /* non-zero? */ + mqch = op1.ch - FP_N_FR; /* set MQ exp */ + } + else op1.ch = mqch = 0; /* clear AC, MQ exp */ + } +else mqch = op1.ch - FP_N_FR; /* set MQ exp */ +return fp_pack (&op1, op1.s, mqch); /* pack AC, MQ */ +} + +/* Double floating divide + + + Notes: + - This is a Taylor series expansion (where ' denotes >> 27): + + (A+B') * (C+D')^-1 = (A+B') * C^-1 - (A+B') * D'* C^-2 +... + + to two terms, which can be rewritten as terms Q1, Q2: + + Q1 = (A+B')/C + Q2' = (R - Q1*D)'/C + + - Tracking the sign of Q2' is complicated: + + Q1 has the sign of the quotient, s_AC ^ s_SR + D has the sign of the divisor, s_SR + R has the sign of the dividend, s_AC + Q1*D sign is s_AC ^ s_SR ^ s^SR = s^AC + Therefore, R and Q1*D have the same sign, s_AC + Q2' sign is s^AC ^ s_SR, which is the sign of the quotient + + - For first divide check, SI is 0 + - For other cases, including second divide check, SI ends up with Q1 + - R-Q1*D is only calculated to the high 27b; using the full 54b + throws off the result + - The second divide must check for divd >= divr, otherwise an extra + bit of quotient would be devloped, throwing off the result + - A late ECO added full post-normalization; single precision divide + does no normalization */ + +uint32 op_dfdv (t_uint64 sr, t_uint64 sr1) +{ +UFP op1, op2; +int32 mqch; +uint32 csign, ac_s; +t_uint64 f1h, f2h, tr, tq1, tq1d, trmq1d, tq2; + +fp_unpack (AC, MQ, 1, &op1); /* unpack AC'MQ */ +fp_unpack (sr, 0, 0, &op2); /* unpack sr only */ +ac_s = op1.s; /* save AC sign */ +op1.s = op1.s ^ op2.s; /* sign of result */ +f1h = FP_HIFRAC (op1.fr); +f2h = FP_HIFRAC (op2.fr); +if (f1h >= (2 * f2h)) { /* |A| >= 2*|C|? */ + SI = 0; /* clear SI */ + return TRAP_F_DVC; /* divide check */ + } +if (f1h == 0) { /* |AC| == 0? */ + SI = MQ = op1.s? SIGN: 0; /* MQ, SI = sign only */ + AC = op1.s? AC_S: 0; /* AC = sign only */ + return 0; /* done */ + } +op1.ch = op1.ch & FP_M_CH; /* remove AC */ +if (f1h >= f2h) { /* |A| >= |C|? */ + op1.fr = op1.fr >> 1; /* denorm AC */ + op1.ch++; + } +op1.ch = op1.ch - op2.ch + FP_BIAS; /* exp of quotient */ +tq1 = fp_fracdiv (op1.fr, op2.fr, &tr); /* |A+B| / |C| */ +tr = tr << FP_N_FR; /* R << 27 */ +tq1d = (tq1 * ((t_uint64) FP_LOFRAC (sr1))) & /* Q1 * D */ + ~((t_uint64) FP_FMASK); /* top 27 bits */ +csign = (tr < tq1d); /* correction sign */ +if (csign) /* |R|<|Q1*D|? compl */ + trmq1d = tq1d - tr; +else trmq1d = tr - tq1d; /* no, subtr ok */ +SI = FP_PACK36 (op1.s, op1.ch, tq1); /* SI has Q1 */ +if (trmq1d >= (2 * op2.fr)) { /* |R-Q1*D| >= 2*|C|? */ + AC = FP_PACK38 (csign ^ ac_s, 0, FP_HIFRAC (trmq1d)); /* AC has R-Q1*D */ + MQ = (csign ^ ac_s)? SIGN: 0; /* MQ = sign only */ + return TRAP_F_DVC; /* divide check */ + } +tq2 = fp_fracdiv (trmq1d, op2.fr, NULL); /* |R-Q1*D| / |C| */ +if (trmq1d >= op2.fr) /* can only gen 27b quo */ + tq2 &= ~((t_uint64) 1); +op1.fr = tq1 << FP_N_FR; /* shift Q1 into place */ +if (csign) /* sub or add Q2 */ + op1.fr = op1.fr - tq2; +else op1.fr = op1.fr + tq2; +fp_norm (&op1); /* normalize */ +if (op1.fr) /* non-zero? */ + mqch = op1.ch - FP_N_FR; +else op1.ch = mqch = 0; /* clear AC, MQ exp */ +return fp_pack (&op1, op1.s, mqch); /* pack AC, MQ */ +} + +/* Floating round */ + +uint32 op_frnd (void) +{ +UFP op; +uint32 spill; + +spill = 0; /* no error */ +if (MQ & B9) { /* MQ9 set? */ + fp_unpack (AC, 0, 1, &op); /* unpack AC */ + op.fr = op.fr + ((t_uint64) (1 << FP_N_FR)); /* round up */ + if (op.fr & FP_FCRY) { /* carry out? */ + op.fr = op.fr >> 1; /* renormalize */ + op.ch++; /* incr exp */ + if (op.ch == (FP_M_CH + 1)) /* ovf with QP = 0? */ + spill = TRAP_F_OVF | TRAP_F_AC; + } + AC = FP_PACK38 (op.s, op.ch, FP_HIFRAC (op.fr)); /* pack AC */ + } +return spill; +} + +/* Fraction divide - 54/27'0 yielding quotient and remainder */ + +t_uint64 fp_fracdiv (t_uint64 dvd, t_uint64 dvr, t_uint64 *rem) +{ +dvr = dvr >> FP_N_FR; +if (rem) + *rem = dvd % dvr; +return (dvd / dvr); +} + +/* Floating point normalize */ + +void fp_norm (UFP *op) +{ +op->fr = op->fr & FP_DFMASK; /* mask fraction */ +if (op->fr == 0) /* zero? */ + return; +while ((op->fr & FP_FNORM) == 0) { /* until norm */ + op->fr = op->fr << 1; /* lsh 1 */ + op->ch--; /* decr exp */ + } +return; +} + +/* Floating point unpack */ + +void fp_unpack (t_uint64 h, t_uint64 l, t_bool q_ac, UFP *op) +{ +if (q_ac) { /* AC? */ + op->s = (h & AC_S)? 1: 0; /* get sign */ + op->ch = (uint32) ((h >> FP_V_CH) & FP_M_ACCH); /* get exp */ + } +else { + op->s = (h & SIGN)? 1: 0; /* no, mem */ + op->ch = (uint32) ((h >> FP_V_CH) & FP_M_CH); + } +op->fr = (((t_uint64) FP_LOFRAC (h)) << FP_N_FR) | /* get frac hi */ + ((t_uint64) FP_LOFRAC (l)); /* get frac lo */ +return; +} + +/* Floating point pack */ + +uint32 fp_pack (UFP *op, uint32 mqs, int32 mqch) +{ +uint32 spill; + +AC = FP_PACK38 (op->s, op->ch, FP_HIFRAC (op->fr)); /* pack AC */ +MQ = FP_PACK36 (mqs, mqch, FP_LOFRAC (op->fr)); /* pack MQ */ +if (op->ch > FP_M_CH) /* check AC exp */ + spill = TRAP_F_OVF | TRAP_F_AC; +else if (op->ch < 0) + spill = TRAP_F_AC; +else spill = 0; +if (mqch > FP_M_CH) /* check MQ exp */ + spill |= (TRAP_F_OVF | TRAP_F_MQ); +else if (mqch < 0) + spill |= TRAP_F_MQ; +return spill; +} diff --git a/I7094/i7094_cpu_old.c b/I7094/i7094_cpu_old.c new file mode 100644 index 00000000..87c46fca --- /dev/null +++ b/I7094/i7094_cpu_old.c @@ -0,0 +1,2439 @@ +/* i7094_cpu.c: IBM 7094 CPU simulator + + Copyright (c) 2003-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu 7094 central processor + + 28-Apr-07 RMS Removed clock initialization + 29-Oct-06 RMS Added additional expanded core instructions + 17-Oct-06 RMS Fixed the fix in halt IO wait loop + 16-Jun-06 RMS Fixed bug in halt IO wait loop + + The register state for the 7094 is: + + AC accumulator + MQ multiplier-quotient register + SI storage indicators + KEYS<0:35> front panel keys (switches) + IC<0:14> instruction counter (called PC here) + XR<0:14>[8] index registers (XR[0] is always 0) + SSW<0:5> sense switches + SLT<0:3> sense lights + OVF AC overflow + MQO MQ overflow + DVC divide check + IOC I/O check + TTRAP transfer trap mode + CTRAP copy trap mode (for 709 compatibility) + FTRAP floating trap mode (off is 704 compatibility) + STRAP select trap mode + STORN storage nullifcation mode + MULTI multi-tag mode (7090 compatibility) + + CTSS required a set of special features: memory extension (to 65K), + protection, and relocation. Additional state: + + USER user mode + INST_BASE instruction memory select (A vs B core) + DATA_BASE data memory select (A vs B core) + IND_RELOC<0:6> relocation value (block number) + IND_START<0:6> start address block + IND_LIMIT<0:6> limit address block + + The 7094 had five instruction formats: memory reference, + memory reference with count, convert, decrement, and immediate. + + 00000000011 11 1111 112 222222222333333 + S12345678901 23 4567 890 123456789012345 + +------------+--+----+---+---------------+ + | opcode |ND|0000|tag| address | memory reference + +------------+--+----+---+---------------+ + + 00000000011 111111 112 222222222333333 + S12345678901 234567 890 123456789012345 + +------------+------+---+---------------+ + | opcode | count|tag| address | memory reference + +------------+------+---+---------------+ with count + + 000000000 11111111 11 2 222222222333333 + S123456789 01234567 89 0 123456789012345 + +----------+--------+--+-+---------------+ + | opcode | count |00|X| address | convert + +----------+--------+--+-+---------------+ + + 00 000000011111111 112 222222222333333 + S12 345678901234567 890 123456789012345 + +---+---------------+---+---------------+ + |opc| decrement |tag| address | decrement + +---+---------------+---+---------------+ + + 00000000011 111111 112222222222333333 + S12345678901 234567 890123456789012345 + +------------+------+------------------+ + | opcode |000000| immediate | immediate + +------------+------+------------------+ + + This routine is the instruction decode routine for the 7094. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until a stop condition occurs. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + illegal instruction + illegal I/O operation for device + illegal I/O operation for channel + breakpoint encountered + nested XEC's exceeding limit + divide check + I/O error in I/O simulator + + 2. Data channel traps. The 7094 is a channel-based system. + Channels can generate traps for errors and status conditions. + Channel trap state: + + ch_flags[0..7] flags for channels A..H + chtr_enab channel trap enables + chtr_inht channel trap inhibit due to trap (cleared by RCT) + chtr_inhi channel trap inhibit due to XEC, ENAB, RCT, RDS, + or WDS (cleared after one instruction) + + Channel traps are summarized in variable chtr_pend. + + 3. Arithmetic. The 7094 uses signed magnitude arithmetic for + integer and floating point calculations, and 2's complement + arithmetic for indexing calculations. + + 4. Adding I/O devices. These modules must be modified: + + i7094_defs.h add device definitions + i7094_io.c add device address mapping + i7094_sys.c add sim_devices table entry +*/ + +#include "i7094_defs.h" + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = (PC | inst_base) + +#define HIST_MIN 64 +#define HIST_MAX (2 << 18) +#define HIST_CH_C 1 /* include channel */ +#define HIST_CH_I 2 /* include IO */ + +#define HALT_IO_LIMIT ((2 << 18) + 1) /* max wait to stop */ + +t_uint64 *M = NULL; /* memory */ +t_uint64 AC = 0; /* AC */ +t_uint64 MQ = 0; /* MQ */ +t_uint64 SI = 0; /* indicators */ +t_uint64 KEYS = 0; /* storage keys */ +uint32 PC = 0; /* PC (IC) */ +uint32 oldPC = 0; /* prior PC */ +uint32 XR[8] = { 0 }; /* index registers */ +uint32 SSW = 0; /* sense switches */ +uint32 SLT = 0; /* sense lights */ +uint32 ch_req = 0; /* channel requests */ +uint32 chtr_pend = 0; /* chan trap pending */ +uint32 chtr_inht = 0; /* chan trap inhibit trap */ +uint32 chtr_inhi = 0; /* chan trap inhibit inst */ +uint32 chtr_enab = 0; /* chan trap enables */ +uint32 mode_ttrap = 0; /* transfer trap mode */ +uint32 mode_ctrap = 0; /* copy trap mode */ +uint32 mode_strap = 0; /* select trap mode */ +uint32 mode_ftrap = 0; /* floating trap mode */ +uint32 mode_storn = 0; /* storage nullification */ +uint32 mode_multi = 0; /* multi-index mode */ +uint32 ind_ovf = 0; /* overflow */ +uint32 ind_mqo = 0; /* MQ overflow */ +uint32 ind_dvc = 0; /* divide check */ +uint32 ind_ioc = 0; /* IO check */ +uint32 cpu_model = I_9X|I_94; /* CPU type */ +uint32 mode_user = 0; /* (CTSS) user mode */ +uint32 ind_reloc = 0; /* (CTSS) relocation */ +uint32 ind_start = 0; /* (CTSS) prot start */ +uint32 ind_limit = 0; /* (CTSS) prot limit */ +uint32 inst_base = 0; /* (CTSS) inst A/B sel */ +uint32 data_base = 0; /* (CTSS) data A/B sel */ +uint32 xec_max = 16; /* XEC chain limit */ +uint32 ht_pend = 0; /* HTR pending */ +uint32 ht_addr = 0; /* HTR address */ +uint32 stop_illop = 1; /* stop on ill op */ +uint32 cpu_astop = 0; /* address stop */ +static uint32 eamask = AMASK; /* (dynamic) addr mask */ + +uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +uint32 hst_ch = 0; /* channel history */ +InstHistory *hst = NULL; /* instruction history */ + +extern uint32 ch_sta[NUM_CHAN]; +extern uint32 ch_flags[NUM_CHAN]; +extern DEVICE mt_dev[NUM_CHAN]; +extern DEVICE ch_dev[NUM_CHAN]; +extern FILE *sim_deb; +extern int32 sim_int_char; +extern int32 sim_interval; +extern int32 sim_switches; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ + +/* Forward and external declarations */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); +t_bool ReadI (uint32 va, t_uint64 *dat); +t_bool Read (uint32 va, t_uint64 *dat); +t_bool Write (uint32 va, t_uint64 dat); +void WriteTA (uint32 pa, uint32 addr); +void WriteTAD (uint32 pa, uint32 addr, uint32 decr); +void TrapXfr (uint32 newpc); +t_bool fp_trap (uint32 spill); +t_bool prot_trap (uint32 decr); +t_bool sel_trap (uint32 va); +t_bool cpy_trap (uint32 va); +uint32 get_xri (uint32 tag); +uint32 get_xrx (uint32 tag); +void put_xr (uint32 tag, uint32 dat); +t_stat cpu_fprint_one_inst (FILE *st, uint32 pc, uint32 rpt, uint32 ea, + t_uint64 ir, t_uint64 ac, t_uint64 mq, t_uint64 si, t_uint64 opnd); + +extern uint32 chtr_eval (uint32 *decr); +extern void op_add (t_uint64 sr); +extern void op_mpy (t_uint64 ac, t_uint64 sr, uint32 sc); +extern t_bool op_div (t_uint64 sr, uint32 sc); +extern uint32 op_fad (t_uint64 sr, t_bool norm); +extern uint32 op_fmp (t_uint64 sr, t_bool norm); +extern uint32 op_fdv (t_uint64); +extern uint32 op_dfad (t_uint64 shi, t_uint64 slo, t_bool norm); +extern uint32 op_dfmp (t_uint64 shi, t_uint64 slo, t_bool norm); +extern uint32 op_dfdv (t_uint64 shi, t_uint64 slo); +extern void op_als (uint32 ea); +extern void op_ars (uint32 ea); +extern void op_lls (uint32 ea); +extern void op_lrs (uint32 ea); +extern void op_lgl (uint32 ea); +extern void op_lgr (uint32 ea); +extern t_stat op_pse (uint32 ea); +extern t_stat op_mse (uint32 ea); +extern t_stat ch_op_ds (uint32 ch, uint32 ds, uint32 unit); +extern t_stat ch_op_nds (uint32 ch, uint32 ds, uint32 unit); +extern t_stat ch_op_start (uint32 ch, uint32 clc, t_bool reset); +extern t_stat ch_op_store (uint32 ch, t_uint64 *dat); +extern t_stat ch_op_store_diag (uint32 ch, t_uint64 *dat); +extern t_stat ch_proc (uint32 ch); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_BINK, STDMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, PC, ASIZE) }, + { ORDATA (AC, AC, 38) }, + { ORDATA (MQ, MQ, 36) }, + { ORDATA (SI, SI, 36) }, + { ORDATA (KEYS, KEYS, 36) }, + { ORDATA (XR1, XR[1], 15) }, + { ORDATA (XR2, XR[2], 15) }, + { ORDATA (XR3, XR[3], 15) }, + { ORDATA (XR4, XR[4], 15) }, + { ORDATA (XR5, XR[5], 15) }, + { ORDATA (XR6, XR[6], 15) }, + { ORDATA (XR7, XR[7], 15) }, + { FLDATA (SS1, SSW, 5) }, + { FLDATA (SS2, SSW, 4) }, + { FLDATA (SS3, SSW, 3) }, + { FLDATA (SS4, SSW, 2) }, + { FLDATA (SS5, SSW, 1) }, + { FLDATA (SS6, SSW, 0) }, + { FLDATA (SL1, SLT, 3) }, + { FLDATA (SL2, SLT, 2) }, + { FLDATA (SL3, SLT, 1) }, + { FLDATA (SL4, SLT, 0) }, + { FLDATA (OVF, ind_ovf, 0) }, + { FLDATA (MQO, ind_mqo, 0) }, + { FLDATA (DVC, ind_dvc, 0) }, + { FLDATA (IOC, ind_ioc, 0) }, + { FLDATA (TTRAP, mode_ttrap, 0) }, + { FLDATA (CTRAP, mode_ctrap, 0) }, + { FLDATA (STRAP, mode_strap, 0) }, + { FLDATA (FTRAP, mode_ftrap, 0) }, + { FLDATA (STORN, mode_storn, 0) }, + { FLDATA (MULTI, mode_multi, 0) }, + { ORDATA (CHREQ, ch_req, NUM_CHAN) }, + { FLDATA (CHTR_PEND, chtr_pend, 0) }, + { FLDATA (CHTR_INHT, chtr_inht, 0) }, + { FLDATA (CHTR_INHI, chtr_inhi, 0) }, + { ORDATA (CHTR_ENAB, chtr_enab, 30) }, + { FLDATA (USERM, mode_user, 0) }, + { FLDATA (IMEM, inst_base, BCORE_V) }, + { FLDATA (DMEM, data_base, BCORE_V) }, + { GRDATA (RELOC, ind_reloc, 8, VA_N_BLK, VA_V_BLK) }, + { GRDATA (START, ind_start, 8, VA_N_BLK, VA_V_BLK) }, + { GRDATA (LIMIT, ind_limit, 8, VA_N_BLK, VA_V_BLK) }, + { ORDATA (OLDPC, oldPC, ASIZE), REG_RO }, + { BRDATA (PCQ, pcq, 8, ASIZE, PCQ_SIZE), REG_RO+REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { FLDATA (HTPEND, ht_pend, 0) }, + { ORDATA (HTADDR, ht_addr, ASIZE) }, + { DRDATA (XECMAX, xec_max, 8), PV_LEFT + REG_NZ }, + { ORDATA (WRU, sim_int_char, 8) }, + { FLDATA (STOP_ILL, stop_illop, 0) }, + { ORDATA (MODEL, cpu_model, 4), REG_HRO }, + { NULL } + }; + +MTAB cpu_mod[] = { + { MTAB_XTD | MTAB_VDV, I_9X|I_94|I_CT, "MODEL", "CTSS", + &cpu_set_model, &cpu_show_model, NULL }, + { MTAB_XTD | MTAB_VDV, I_9X|I_94, NULL, "7094", + &cpu_set_model, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, I_9X, NULL, "7090", + &cpu_set_model, NULL, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, PASIZE, 1, 8, 36, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, + NULL, DEV_DEBUG + }; + +/* Instruction decode table */ + +const uint8 op_flags[1024] = { + I_XN , 0 , 0 , 0 , /* +000 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , I_XN|I_9X , I_XN , 0 , /* +020 */ + I_XN , 0 , I_XN , I_XN , + I_XN , I_XN , I_XN , I_XN , + 0 , 0 , 0 , 0 , + I_XN|I_9X , I_9X , I_XN|I_9X , I_9X , /* +040 */ + I_9X , 0 , I_XN|I_9X , 0 , + 0 , I_9X , 0 , 0 , + I_9X , I_9X , I_9X , I_9X , + I_XN , I_XN , I_XN , I_XN , /* +060 */ + I_XN , I_XN , I_XN , I_XN , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , I_XN|I_CT , 0 , 0 , /* +100 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_9X , I_9X , I_9X , I_9X , + I_XN , 0 , 0 , 0 , /* +120 */ + 0 , 0 , 0 , 0 , + 0 , I_9X , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , 0 , 0 , 0 , /* +140 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , I_XN|I_9X , I_XN|I_9X , 0 , /* +160 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , 0 , 0 , 0 , /* +200 */ + I_XNR , I_XNR , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, I_XNR , 0 , 0 , /* +220 */ + I_XNR|I_9X, I_XNR , I_XNR|I_9X, I_XNR , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, I_XNR , 0 , 0 , /* +240 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , I_XND|I_94, 0 , 0 , /* +260 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , I_XND|I_94, I_XNR , I_XND|I_94, /* +300 */ + I_XNR|I_9X, I_XND|I_94, I_XNR|I_9X, I_XND|I_94, + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, 0 , I_XNR|I_9X, 0 , /* +320 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , 0 , 0 , 0 , /* +340 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , I_XNR , 0 , 0 , /* +360 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , I_XNR|I_9X, I_XNR , 0 , /* +400 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* +420 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, I_XNR|I_9X, I_XNR|I_9X, I_XNR|I_94, /* +440 */ + I_XNR|I_9X, I_XNR|I_9X, I_XNR|I_9X, 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_9X , 0 , 0 , 0 , /* +460 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, 0 , I_XNR , 0 , /* +500 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , 0 , I_XNR , 0 , /* +520 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_R , I_R , 0 , 0 , + I_XN , I_XN , I_XN , I_XN , /* +540 */ + I_XN , I_XN , I_XN , I_XN , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , 0 , I_XNR|I_CT, 0 , /* +560 */ + I_XNR , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , I_XN , I_XN , 0 , /* +600 */ + I_XN|I_9X , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , I_XNR , I_XNR , 0 , /* +620 */ + 0 , I_XNR|I_9X, 0 , 0 , + I_XNR|I_9X, 0 , 0 , 0 , + I_R , 0 , I_R|I_94 , 0 , + I_XN , I_XN , I_XN , I_XN , /* +640 */ + I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* +660 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_9X , 0 , 0 , 0 , /* +700 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* +720 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* +740 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , I_94 , 0 , + I_X , 0 , I_X , I_X , /* +760 */ + I_X , I_X , I_X , I_X , + I_X , I_X , I_X , 0 , + 0 , 0 , 0 , 0 , + + I_XN , 0 , 0 , 0 , /* -000 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , I_XN|I_9X , I_XN , 0 , /* -020 */ + I_XN , 0 , I_XN , I_XN , + I_XN , I_XN , I_XN , I_XN , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -040 */ + 0 , 0 , I_9X , 0 , + 0 , I_9X , 0 , 0 , + I_9X , I_9X , I_9X , I_9X , + I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , /* -060 */ + I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , I_XN|I_CT , 0 , 0 , /* -100 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_9X , I_9X , I_9X , I_9X , + I_XN , 0 , 0 , 0 , /* -120 */ + 0 , 0 , 0 , 0 , + I_9X , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN|I_9X , 0 , 0 , 0 , /* -140 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_9X , I_9X , I_9X , I_9X , + 0 , 0 , 0 , 0 , /* -160 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, 0 , 0 , 0 , /* -200 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -220 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XND|I_94, I_XND|I_94, 0 , 0 , /* -240 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , I_XND|I_94, 0 , 0 , /* -260 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , I_XND|I_94, I_XNR , I_XND|I_94, /* -300 */ + I_XNR|I_9X, I_XND|I_94, I_XNR|I_9X, I_XND|I_94, + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , 0 , 0 , 0 , /* -320 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , 0 , 0 , 0 , /* -340 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -360 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, 0 , 0 , 0 , /* -400 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -420 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -440 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -460 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , I_XNR , 0 , 0 , /* -500 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, 0 , 0 , 0 , /* -520 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_R , I_R , 0 , 0 , + I_XN , I_XN , I_XN , I_XN , /* -540 */ + I_XN , I_XN , I_XNR , I_XN , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -560 */ + I_XNR|I_CT, 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , 0 , I_XNR|I_9X, I_XN|I_94 , /* -600 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, 0 , 0 , 0 , /* -620 */ + 0 , I_XNR , 0 , 0 , + 0 , 0 , 0 , 0 , + I_R , 0 , I_R|I_94 , 0 , + I_XN , I_XN , I_XN , I_XN , /* -640 */ + I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -660 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_9X , 0 , 0 , 0 , /* -700 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -720 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -740 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , I_94 , 0 , + I_X , I_X|I_CT , 0 , I_X , /* -760 */ + 0 , I_X , 0 , 0 , + 0 , 0 , I_X , I_X , + I_9X , 0 , 0 , 0 + }; + +/* Instruction execution routine */ + +t_stat sim_instr (void) +{ +t_stat reason = SCPE_OK; +t_uint64 IR, SR, t, t1, t2, sr1; +uint32 op, fl, tag, tagi, addr, ea; +uint32 ch, dec, xr, xec_cnt, trp; +uint32 i, j, sc, s1, s2, spill; +t_bool tracing; + +/* Restore register state */ + +ch_set_map (); /* set dispatch map */ +if (!(cpu_model & (I_94|I_CT))) /* ~7094? MTM always on */ + mode_multi = 1; +eamask = mode_storn? A704_MASK: AMASK; /* set eff addr mask */ +inst_base = inst_base & ~AMASK; /* A/B sel is 1b */ +data_base = data_base & ~AMASK; +ind_reloc = ind_reloc & VA_BLK; /* canonical form */ +ind_start = ind_start & VA_BLK; +ind_limit = (ind_limit & VA_BLK) | VA_OFF; +chtr_pend = chtr_eval (NULL); /* eval chan traps */ +tracing = ((hst_lnt != 0) || DEBUG_PRS (cpu_dev)); + +if (ht_pend) { /* HTR pending? */ + oldPC = (PC - 1) & AMASK; + ht_pend = 0; /* clear flag */ + PCQ_ENTRY; + if (mode_ttrap) { /* trap? */ + WriteTA (TRAP_STD_SAV, oldPC); /* save PC */ + TrapXfr (TRAP_TRA_PC); /* trap */ + } + else PC = ht_addr; /* branch */ + } + +/* Main instruction fetch/decode loop */ + +while (reason == SCPE_OK) { /* loop until error */ + + if (cpu_astop) { /* debug stop? */ + cpu_astop = 0; + reason = SCPE_STOP; + break; + } + + if (sim_interval <= 0) { /* intv cnt expired? */ + if (reason = sim_process_event ()) /* process events */ + break; + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + } + + for (i = 0; ch_req && (i < NUM_CHAN); i++) { /* loop thru channels */ + if (ch_req & REQ_CH (i)) { /* channel request? */ + if (reason = ch_proc (i)) + break; + } + chtr_pend = chtr_eval (NULL); + if (reason) /* error? */ + break; + } + + if (chtr_pend) { /* channel trap? */ + addr = chtr_eval (&trp); /* get trap info, clr */ + chtr_inht = 1; /* inhibit traps */ + chtr_pend = 0; /* no trap pending */ + WriteTAD (addr, PC, trp); /* wr trap addr,flag */ + IR = ReadP (addr + 1); /* get trap instr */ + oldPC = PC; /* save current PC */ + } + + else { + if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + if (chtr_inhi) { /* 1 cycle inhibit? */ + chtr_inhi = 0; /* clear */ + chtr_pend = chtr_eval (NULL); /* re-evaluate */ + } + oldPC = PC; /* save current PC */ + PC = (PC + 1) & eamask; /* increment PC */ + if (!ReadI (oldPC, &IR)) /* get inst; trap? */ + continue; + } + + sim_interval = sim_interval - 1; + xec_cnt = 0; /* clear XEC cntr */ + + XEC: + + tag = GET_TAG (IR); /* get tag */ + addr = (uint32) IR & eamask; /* get base addr */ + +/* Decrement format instructions */ + + if (IR & INST_T_DEC) { /* decrement type? */ + op = GET_OPD (IR); /* get opcode */ + dec = GET_DEC (IR); /* get decrement */ + xr = get_xrx (tag); /* get xr, upd MTM */ + if (tracing) { /* trace or history? */ + if (hst_lnt) /* history enabled? */ + cpu_ent_hist (oldPC|HIST_PC, xr, IR, 0); + if (DEBUG_PRS (cpu_dev)) + cpu_fprint_one_inst (sim_deb, oldPC|HIST_PC, 0, xr, + IR, AC, MQ, SI, 0); + } + switch (op) { + + case 01: /* TXI */ + put_xr (tag, xr + dec); /* xr += decr */ + PCQ_ENTRY; + if (mode_ttrap) { /* trap? */ + WriteTA (TRAP_STD_SAV, oldPC); /* save PC */ + TrapXfr (TRAP_TRA_PC); /* trap */ + } + else PC = addr; /* branch */ + break; + + case 02: /* TIX */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if (xr > dec) { /* if xr > decr */ + put_xr (tag, xr - dec); /* xr -= decr */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = addr; /* branch */ + } + break; + + case 03: /* TXH */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if (xr > dec) { /* if xr > decr */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = addr; /* branch */ + } + break; + + case 05: /* STR */ + WriteTA (TRAP_STD_SAV, PC); /* save inst+1 */ + PCQ_ENTRY; + PC = TRAP_STR_PC; /* branch to 2 */ + break; + + case 06: /* TNX */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if (xr > dec) /* if xr > decr */ + put_xr (tag, xr - dec); + else { /* xr -= decr */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = addr; /* branch */ + } + break; + + case 07: /* TXL */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if (xr <= dec) { /* if xr <= decr */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = addr; /* branch */ + } + break; + } + } /* end if */ + +/* Normal format instructions */ + + else { + op = GET_OPC (IR); /* get opcode */ + fl = op_flags[op]; /* get flags */ + if (fl & I_MODEL & ~cpu_model) { /* invalid for model? */ + if (stop_illop) /* possible stop */ + reason = STOP_ILLEG; + continue; + } + if (tag && (fl & I_X)) /* tag and indexable? */ + ea = (addr - get_xri (tag)) & eamask; /* do indexing */ + else ea = addr; + if (TST_IND (IR) && (fl & I_N)) { /* indirect? */ + if (!ReadI (ea, &SR)) /* get ind; trap? */ + continue; + addr = (uint32) SR & eamask; /* get address */ + tagi = GET_TAG (SR); /* get tag */ + if (tagi) /* tag? */ + ea = (addr - get_xri (tagi)) & eamask; /* do indexing */ + else ea = addr; + } + if ((fl & I_R) && !Read (ea, &SR)) /* read opnd; trap? */ + continue; + else if (fl & I_D) { /* double prec? */ + if ((ea & 1) && fp_trap (TRAP_F_ODD)) + continue; + if (!Read (ea, &SR)) /* SR gets high */ + continue; + if (!Read (ea | 1, &sr1)) /* "sr1" gets low */ + continue; + } + if (tracing) { /* tracing or history? */ + if (hst_lnt) /* history enabled? */ + cpu_ent_hist (oldPC|HIST_PC, ea, IR, SR); + if (DEBUG_PRS (cpu_dev)) + cpu_fprint_one_inst (sim_deb, oldPC|HIST_PC, 0, ea, + IR, AC, MQ, SI, SR); + } + switch (op) { /* case on opcode */ + +/* Positive instructions */ + + case 00000: /* HTR */ + case 01000: /* also -HTR */ + if (prot_trap (0)) /* user mode? */ + break; + ht_pend = 1; /* transfer pending */ + ht_addr = ea; /* save address */ + reason = STOP_HALT; /* halt if I/O done */ + break; + + case 00020: /* TRA */ + case 01020: /* also -TRA */ + PCQ_ENTRY; + if (mode_ttrap) { /* trap? */ + WriteTA (TRAP_STD_SAV, oldPC); /* save PC */ + TrapXfr (TRAP_TRA_PC); /* trap */ + } + else PC = ea; /* branch */ + break; + + case 00021: /* TTR */ + PCQ_ENTRY; + PC = ea; /* branch, no trap */ + break; + + case 00040: /* TLQ */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + s1 = (AC & AC_S)? 1: 0; /* get AC, MQ sign, */ + s2 = (MQ & SIGN)? 1: 0; /* magnitude */ + t1 = AC & AC_MMASK; + t2 = MQ & MMASK; /* signs differ? */ + if ((s1 != s2)? s2: /* y, br if MQ- */ + ((t1 != t2) && (s2 ^ (t1 > t2)))) { /* n, br if sgn-^AC>MQ */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + } + break; + + case 00041: /* IIA */ + SI = SI ^ (AC & DMASK); + break; + + case 00042: /* TIO */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if ((SI & AC) == (AC & DMASK)) { /* if ind on */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + } + break; + + case 00043: /* OAI */ + SI = SI | (AC & DMASK); + break; + + case 00044: /* PAI */ + SI = AC & DMASK; + break; + + case 00046: /* TIF */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if ((SI & AC) == 0) { /* if ind off */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + } + break; + + case 00051: /* IIR */ + SI = SI ^ (IR & RMASK); + break; + + case 00054: /* RFT */ + t = IR & RMASK; + if ((SI & t) == 0) /* if ind off, skip */ + PC = (PC + 1) & eamask; + break; + + case 00055: /* SIR */ + SI = SI | (IR & RMASK); + break; + + case 00056: /* RNT */ + t = IR & RMASK; + if ((SI & t) == t) /* if ind on, skip */ + PC = (PC + 1) & eamask; + break; + + case 00057: /* RIR */ + SI = SI & ~(IR & RMASK); + break; + + case 00074: /* TSX */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if (tag) /* save -inst loc */ + put_xr (tag, ~oldPC + 1); + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + break; + + case 00100: /* TZE */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if ((AC & AC_MMASK) == 0) { /* if AC Q,P,1-35 = 0 */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + } + break; + + case 00101: /* (CTSS) TIA */ + if (prot_trap (0)) /* not user mode? */ + break; + PCQ_ENTRY; + PC = ea; + inst_base = 0; + break; + + case 00114: case 00115: case 00116: case 00117: /* CVR */ + sc = GET_CCNT (IR); + SR = ea; + while (sc) { + ea = (uint32) ((AC & 077) + SR) & eamask; + if (!Read (ea, &SR)) + break; + AC = (AC & AC_S) | ((AC >> 6) & 0017777777777) | + (SR & 0770000000000); + sc--; + } + if ((sc == 0) && (IR & INST_T_CXR1)) + put_xr (1, (uint32) SR); + break; + + case 00120: /* TPL */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if ((AC & AC_S) == 0) { /* if AC + */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + } + break; + + case 00131: /* XCA */ + t = MQ; + MQ = (AC & MMASK) | ((AC & AC_S)? SIGN: 0); + AC = (t & MMASK) | ((t & SIGN)? AC_S: 0); + break; + + case 00140: /* TOV */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if (ind_ovf) { /* if overflow */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + ind_ovf = 0; /* clear overflow */ + } + break; + + case 00161: /* TQO */ + if (!mode_ftrap) { /* only in 704 mode */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if (ind_mqo) { /* if MQ overflow */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + ind_mqo = 0; /* clear overflow */ + } + } + break; + + case 00162: /* TQP */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if ((MQ & SIGN) == 0) { /* if MQ + */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + } + break; + + case 00200: /* MPY */ + op_mpy (0, SR, 043); + break; + + case 00204: /* VLM */ + case 00205: /* for diagnostic */ + sc = GET_VCNT (IR); + op_mpy (0, SR, sc); + break; + + case 00220: /* DVH */ + if (op_div (SR, 043)) { + ind_dvc = 1; + if (!prot_trap (0)) + reason = STOP_DIVCHK; + } + break; + + case 00221: /* DVP */ + if (op_div (SR, 043)) + ind_dvc = 1; + break; + + case 00224: /* VDH */ + case 00226: /* for diagnostic */ + sc = GET_VCNT (IR); + if (op_div (SR, sc)) { + ind_dvc = 1; + if (!prot_trap (0)) + reason = STOP_DIVCHK; + } + break; + + case 00225: /* VDP */ + case 00227: /* for diagnostic */ + sc = GET_VCNT (IR); + if (op_div (SR, sc)) + ind_dvc = 1; + break; + + case 00240: /* FDH */ + spill = op_fdv (SR); + if (spill == TRAP_F_DVC) { + ind_dvc = 1; + if (!prot_trap (0)) + reason = STOP_DIVCHK; + } + else if (spill) + fp_trap (spill); + break; + + case 00241: /* FDP */ + spill = op_fdv (SR); + if (spill == TRAP_F_DVC) + ind_dvc = 1; + else if (spill) + fp_trap (spill); + break; + + case 00260: /* FMP */ + spill = op_fmp (SR, 1); /* MQ * SR */ + if (spill) + fp_trap (spill); + break; + + case 00261: /* DFMP */ + spill = op_dfmp (SR, sr1, 1); + if (spill) + fp_trap (spill); + break; + + case 00300: /* FAD */ + spill = op_fad (SR, 1); + if (spill) + fp_trap (spill); + break; + + case 00301: /* DFAD */ + spill = op_dfad (SR, sr1, 1); + if (spill) + fp_trap (spill); + break; + + case 00302: /* FSB */ + spill = op_fad (SR ^ SIGN, 1); + if (spill) + fp_trap (spill); + break; + + case 00303: /* DFSB */ + spill = op_dfad (SR ^ SIGN, sr1, 1); + if (spill) + fp_trap (spill); + break; + + case 00304: /* FAM */ + spill = op_fad (SR & ~SIGN, 1); + if (spill) + fp_trap (spill); + break; + + case 00305: /* DFAM */ + spill = op_dfad (SR & ~SIGN, sr1, 1); + if (spill) + fp_trap (spill); + break; + + case 00306: /* FSM */ + spill = op_fad (SR | SIGN, 1); + if (spill) + fp_trap (spill); + break; + + case 00307: /* DFSM */ + spill = op_dfad (SR | SIGN, sr1, 1); + if (spill) + fp_trap (spill); + break; + + case 00320: /* ANS */ + SR = AC & SR; + Write (ea, SR); + break; + + case 00322: /* ERA */ + AC = (AC ^ SR) & DMASK; /* AC S,Q cleared */ + break; + + case 00340: /* CAS */ + s1 = (AC & AC_S)? 1: 0; /* get AC, MQ signs, */ + s2 = (SR & SIGN)? 1: 0; + t1 = AC & AC_MMASK; /* magnitudes */ + t2 = SR & MMASK; + if (s1 ^ s2) { /* diff signs? */ + if (s1) /* AC < mem? skip 2 */ + PC = (PC + 2) & eamask; + } + else if (t1 == t2) /* equal? skip 1 */ + PC = (PC + 1) & eamask; + else if ((t1 < t2) ^ s1) /* AC < mem, AC +, or */ + PC = (PC + 2) & eamask; /* AC > mem, AC -? */ + break; + + case 00361: /* ACL */ + t = (AC + SR) & DMASK; /* AC P,1-35 + SR */ + if (t < SR) /* end around carry */ + t = (t + 1) & DMASK; + AC = (AC & (AC_S | AC_Q)) | t; /* preserve AC S,Q */ + break; + + case 00400: /* ADD */ + op_add (SR); + break; + + case 00401: /* ADM */ + op_add (SR & MMASK); + break; + + case 00402: /* SUB */ + op_add (SR ^ SIGN); + break; + + case 00420: /* HPR */ + if (prot_trap (0)) /* user mode? */ + break; + reason = STOP_HALT; /* halt if I/O done */ + break; + + case 00440: /* IIS */ + SI = SI ^ SR; + break; + + case 00441: /* LDI */ + SI = SR; + break; + + case 00442: /* OSI */ + SI = SI | SR; + break; + + case 00443: /* DLD */ + AC = (SR & MMASK) | ((SR & SIGN)? AC_S: 0); /* normal load */ + if (!Read (ea | 1, &SR)) /* second load */ + break; + MQ = SR; + if (ea & 1) /* trap after exec */ + fp_trap (TRAP_F_ODD); + break; + + case 00444: /* OFT */ + if ((SI & SR) == 0) /* skip if ind off */ + PC = (PC + 1) & eamask; + break; + + case 00445: /* RIS */ + SI = SI & ~SR; + break; + + case 00446: /* ONT */ + if ((SI & SR) == SR) /* skip if ind on */ + PC = (PC + 1) & eamask; + break; + + case 00460: /* LDA (704) */ + cpy_trap (PC); + break; + + case 00500: /* CLA */ + AC = (SR & MMASK) | ((SR & SIGN)? AC_S: 0); + break; + + case 00502: /* CLS */ + AC = (SR & MMASK) | ((SR & SIGN)? 0: AC_S); + break; + + case 00520: /* ZET */ + if ((SR & MMASK) == 0) /* skip if M 1-35 = 0 */ + PC = (PC + 1) & eamask; + break; + + case 00522: /* XEC */ + if (xec_cnt++ >= xec_max) { /* xec chain limit? */ + reason = STOP_XEC; /* stop */ + break; + } + IR = SR; /* operand is new inst */ + chtr_inhi = 1; /* delay traps */ + chtr_pend = 0; /* no trap now */ + goto XEC; /* start over */ + + case 00534: /* LXA */ + if (tag) /* M addr -> xr */ + put_xr (tag, (uint32) SR); + break; + + case 00535: /* LAC */ + if (tag) /* -M addr -> xr */ + put_xr (tag, NEG ((uint32) SR)); + break; + + case 00560: /* LDQ */ + MQ = SR; + break; + + case 00562: /* (CTSS) LRI */ + if (prot_trap (0)) /* user mode? */ + break; + ind_reloc = ((uint32) SR) & VA_BLK; + break; + + case 00564: /* ENB */ + if (prot_trap (0)) /* user mode? */ + break; + chtr_enab = (uint32) SR; /* set enables */ + chtr_inht = 0; /* clear inhibit */ + chtr_inhi = 1; /* 1 cycle delay */ + chtr_pend = 0; /* no traps now */ + break; + + case 00600: /* STZ */ + Write (ea, 0); + break; + + case 00601: /* STO */ + SR = (AC & MMASK) | ((AC & AC_S)? SIGN: 0); + Write (ea, SR); + break; + + case 00602: /* SLW */ + Write (ea, AC & DMASK); + break; + + case 00604: /* STI */ + Write (ea, SI); + break; + + case 00621: /* STA */ + SR = (SR & ~AMASK) | (AC & AMASK); + Write (ea, SR); + break; + + case 00622: /* STD */ + SR = (SR & ~XMASK) | (AC & XMASK); + Write (ea, SR); + break; + + case 00625: /* STT */ + SR = (SR & ~TMASK) | (AC & TMASK); + Write (ea, SR); + break; + + case 00630: /* STP */ + SR = (SR & ~PMASK) | (AC & PMASK); + Write (ea, SR); + break; + + case 00634: /* SXA */ + SR = (SR & ~AMASK) | /* xr -> M addr */ + ((t_uint64) get_xrx (tag)); + Write (ea, SR); + break; + + case 00636: /* SCA */ + SR = (SR & ~AMASK) | /* -xr -> M addr */ + ((t_uint64) (NEG (get_xrx (tag)) & AMASK)); + Write (ea, SR); + break; + + case 00700: /* CPY (704) */ + cpy_trap (PC); + break; + + case 00734: /* PAX */ + if (tag) /* AC addr -> xr */ + put_xr (tag, (uint32) AC); + break; + + case 00737: /* PAC */ + if (tag) /* -AC addr -> xr */ + put_xr (tag, NEG ((uint32) AC)); + break; + + case 00754: /* PXA */ + AC = get_xrx (tag); /* xr -> AC */ + break; + + case 00756: /* PCA */ + AC = NEG (get_xrx (tag)) & AMASK; /* -xr -> AC */ + break; + + case 00760: /* PSE */ + if (prot_trap (0)) /* user mode? */ + break; + reason = op_pse (ea); + break; + + case 00761: /* NOP */ + break; + + case 00763: /* LLS */ + op_lls (ea); + break; + + case 00765: /* LRS */ + op_lrs (ea); + break; + + case 00767: /* ALS */ + op_als (ea); + break; + + case 00771: /* ARS */ + op_ars (ea); + break; + + case 00774: /* AXT */ + if (tag) /* IR addr -> xr */ + put_xr (tag, addr); + break; + +/* Negative instructions */ + + case 01021: /* ESNT */ + mode_storn = 1; /* enter nullification */ + PCQ_ENTRY; + PC = ea; /* branch, no trap */ + break; + + case 01042: /* RIA */ + SI = SI & ~AC; + break; + + case 01046: /* PIA */ + AC = SI; + break; + + case 01051: /* IIL */ + SI = SI ^ ((IR & RMASK) << 18); + break; + + case 01054: /* LFT */ + t = (IR & RMASK) << 18; + if ((SI & t) == 0) /* if ind off, skip */ + PC = (PC + 1) & eamask; + break; + + case 01055: /* SIL */ + SI = SI | ((IR & RMASK) << 18); + break; + + case 01056: /* LNT */ + t = (IR & RMASK) << 18; + if ((SI & t) == t) /* if ind on, skip */ + PC = (PC + 1) & eamask; + break; + + case 01057: /* RIL */ + SI = SI & ~((IR & RMASK) << 18); + break; + + case 01100: /* TNZ */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if ((AC & AC_MMASK) != 0) { /* if AC != 0 */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + } + break; + + case 01101: /* (CTSS) TIB */ + if (prot_trap (0)) /* user mode? */ + break; + PCQ_ENTRY; + PC = ea; + mode_user = 1; + inst_base = BCORE_BASE; + break; + + case 01114: case 01115: case 01116: case 01117: /* CAQ */ + sc = GET_CCNT (IR); + SR = ea; + while (sc) { + ea = (uint32) ((MQ >> 30) + SR) & eamask; + if (!Read (ea, &SR)) + break; + MQ = ((MQ << 6) & DMASK) | (MQ >> 30); + AC = (AC & AC_S) | ((AC + SR) & AC_MMASK); + sc--; + } + if ((sc == 0) && (IR & INST_T_CXR1)) + put_xr (1, (uint32) SR); + break; + + case 01120: /* TMI */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if ((AC & AC_S) != 0) { /* if AC - */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + } + break; + + case 01130: /* XCL */ + t = MQ; + MQ = AC & DMASK; + AC = t; + break; + + case 01140: /* TNO */ + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if (!ind_ovf) { /* if no overflow */ + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + } + ind_ovf = 0; /* clear overflow */ + break; + + case 01154: case 01155: case 01156: case 01157: /* CRQ */ + sc = GET_CCNT (IR); + SR = ea; + while (sc) { + ea = (uint32) ((MQ >> 30) + SR) & eamask; + if (!Read (ea, &SR)) + break; + MQ = ((MQ << 6) & DMASK) | (SR >> 30); + sc--; + } + if ((sc == 0) && (IR & INST_T_CXR1)) + put_xr (1, (uint32) SR); + break; + + case 01200: /* MPR */ + op_mpy (0, SR, 043); + if (MQ & B1) + AC = (AC & AC_S) | ((AC + 1) & AC_MMASK); + break; + + case 01240: /* DFDH */ + spill = op_dfdv (SR, sr1); + if (spill == TRAP_F_DVC) { + ind_dvc = 1; + if (!prot_trap (0)) + reason = STOP_DIVCHK; + } + else if (spill) + fp_trap (spill); + break; + + case 01241: /* DFDP */ + spill = op_dfdv (SR, sr1); + if (spill == TRAP_F_DVC) + ind_dvc = 1; + else if (spill) + fp_trap (spill); + break; + + case 01260: /* UFM */ + spill = op_fmp (SR, 0); + if (spill) + fp_trap (spill); + break; + + case 01261: /* DUFM */ + spill = op_dfmp (SR, sr1, 0); + if (spill) + fp_trap (spill); + break; + + case 01300: /* UFA */ + spill = op_fad (SR, 0); + if (spill) + fp_trap (spill); + break; + + case 01301: /* DUFA */ + spill = op_dfad (SR, sr1, 0); + if (spill) + fp_trap (spill); + break; + + case 01302: /* UFS */ + spill = op_fad (SR ^ SIGN, 0); + if (spill) + fp_trap (spill); + break; + + case 01303: /* DUFS */ + spill = op_dfad (SR ^ SIGN, sr1, 0); + if (spill) + fp_trap (spill); + break; + + case 01304: /* UAM */ + spill = op_fad (SR & ~SIGN, 0); + if (spill) + fp_trap (spill); + break; + + case 01305: /* DUAM */ + spill = op_dfad (SR & ~SIGN, sr1, 0); + if (spill) + fp_trap (spill); + break; + + case 01306: /* USM */ + spill = op_fad (SR | SIGN, 0); + if (spill) + fp_trap (spill); + break; + + case 01307: /* DUSM */ + spill = op_dfad (SR | SIGN, sr1, 0); + if (spill) + fp_trap (spill); + break; + + case 01320: /* ANA */ + AC = AC & SR; + break; + + case 01340: /* LAS */ + t = AC & AC_MMASK; /* AC Q,P,1-35 */ + if (t < SR) + PC = (PC + 2) & eamask; + else if (t == SR) + PC = (PC + 1) & eamask; + break; + + case 01400: /* SBM */ + op_add (SR | SIGN); + break; + + case 01500: /* CAL */ + AC = SR; + break; + + case 01501: /* ORA */ + AC = AC | SR; + break; + + case 01520: /* NZT */ + if ((SR & MMASK) != 0) + PC = (PC + 1) & eamask; + break; + + case 01534: /* LXD */ + if (tag) /* M decr -> xr */ + put_xr (tag, GET_DEC (SR)); + break; + + case 01535: /* LDC */ + if (tag) /* -M decr -> xr */ + put_xr (tag, NEG (GET_DEC (SR))); + break; + + case 01564: /* (CTSS) LPI */ + if (prot_trap (0)) /* user mode? */ + break; + ind_start = ((uint32) SR) & VA_BLK; + ind_limit = (GET_DEC (SR) & VA_BLK) | VA_OFF; + break; + + case 01600: /* STQ */ + Write (ea, MQ); + break; + + case 01602: /* ORS */ + SR = SR | (AC & DMASK); + Write (ea, SR); + break; + + case 01603: /* DST */ + SR = (AC & MMASK) | ((AC & AC_S)? SIGN: 0); + if (!Write (ea, SR)) + break; + Write ((ea + 1) & eamask, MQ); + break; + + case 01620: /* SLQ */ + SR = (SR & RMASK) | (MQ & LMASK); + Write (ea, SR); + break; + + case 01625: /* STL */ + SR = (SR & ~AMASK) | PC; + Write (ea, SR); + break; + + case 01634: /* SXD */ + SR = (SR & ~XMASK) | /* xr -> M decr */ + (((t_uint64) get_xrx (tag)) << INST_V_DEC); + Write (ea, SR); + break; + + case 01636: /* SCD */ + SR = (SR & ~XMASK) | /* -xr -> M decr */ + (((t_uint64) (NEG (get_xrx (tag)) & AMASK)) << INST_V_DEC); + Write (ea, SR); + break; + + case 01700: /* CAD (704) */ + cpy_trap (PC); + break; + + case 01734: /* PDX */ + if (tag) /* AC decr -> xr */ + put_xr (tag, GET_DEC (AC)); + break; + + case 01737: /* PDC */ + if (tag) /* -AC decr -> xr */ + put_xr (tag, NEG (GET_DEC (AC))); + break; + + case 01754: /* PXD */ + AC = ((t_uint64) get_xrx (tag)) << INST_V_DEC; + break; /* xr -> AC decr */ + + case 01756: /* PCD */ + AC = ((t_uint64) (NEG (get_xrx (tag)) & AMASK)) << INST_V_DEC; + break; /* -xr -> AC decr */ + + case 01760: /* MSE */ + if (prot_trap (0)) /* user mode? */ + break; + reason = op_mse (ea); + break; + + case 01761: /* (CTSS) ext core */ + if (prot_trap (0)) /* user mode? */ + break; + if (ea == 041) /* SEA? */ + data_base = 0; + else if (ea == 042) /* SEB? */ + data_base = BCORE_BASE; + else if (ea == 043) { /* IFT? */ + if (inst_base == 0) + PC = (PC + 1) & eamask; + } + else if (ea == 044) { /* EFT? */ + if (data_base == 0) + PC = (PC + 1) & eamask; + } + else if (stop_illop) + reason = STOP_ILLEG; + break; + + case 01763: /* LGL */ + op_lgl (ea); + break; + + case 01765: /* LGR */ + op_lgr (ea); + break; + + case 01773: /* RQL */ + sc = (ea & SCMASK) % 36; + if (sc) + MQ = ((MQ << sc) | (MQ >> (36 - sc))) & DMASK; + break; + + case 01774: /* AXC */ + if (tag) /* -IR addr -> xr */ + put_xr (tag, NEG (addr)); + break; + +/* IO instructions */ + + case 00022: case 00024: case 00026: /* TRCx */ + case 01022: case 01024: case 01026: + if (prot_trap (0)) /* user mode? */ + break; + ch = ((op & 077) - 00022) | ((op >> 9) & 01); + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if (!BIT_TST (chtr_enab, CHTR_V_TRC + ch) && + (ch_flags[ch] & CHF_TRC)) { + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + ch_flags[ch] = ch_flags[ch] & ~CHF_TRC; + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + } + break; + + case 00027: case 01027: + if (prot_trap (0)) /* user mode? */ + break; + ch = 6 + ((op >> 9) & 01); + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if (!BIT_TST (chtr_enab, CHTR_V_TRC + ch) && + (ch_flags[ch] & CHF_TRC)) { + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + ch_flags[ch] = ch_flags[ch] & ~CHF_TRC; + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + } + break; + + case 00030: case 00031: case 00032: case 00033: /* TEFx */ + case 01030: case 01031: case 01032: case 01033: + if (prot_trap (0)) /* user mode? */ + break; + ch = ((op & 03) << 1) | ((op >> 9) & 01); + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if (!BIT_TST (chtr_enab, CHTR_V_CME + ch) && + (ch_flags[ch] & CHF_EOF)) { + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + ch_flags[ch] = ch_flags[ch] & ~CHF_EOF; + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + } + break; + + case 00060: case 00061: case 00062: case 00063: /* TCOx */ + case 00064: case 00065: case 00066: case 00067: + if (prot_trap (0)) /* user mode? */ + break; + ch = op & 07; + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if (ch_sta[ch] != CHXS_IDLE) { + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + } + break; + + case 01060: case 01061: case 01062: case 01063: /* TCNx */ + case 01064: case 01065: case 01066: case 01067: + if (prot_trap (0)) /* user mode? */ + break; + ch = op & 07; + if (mode_ttrap) + WriteTA (TRAP_STD_SAV, oldPC); + if (ch_sta[ch] == CHXS_IDLE) { + PCQ_ENTRY; + if (mode_ttrap) /* trap? */ + TrapXfr (TRAP_TRA_PC); + else PC = ea; /* branch */ + } + break; + + case 00540: case 00541: case 00542: case 00543: /* RCHx */ + case 01540: case 01541: case 01542: case 01543: + if (prot_trap (0)) /* user mode? */ + break; + ch = ((op & 03) << 1) | ((op >> 9) & 01); + reason = ch_op_start (ch, ea, TRUE); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00544: case 00545: case 00546: case 00547: /* LCHx */ + case 01544: case 01545: case 01546: case 01547: + if (prot_trap (0)) /* user mode? */ + break; + ch = ((op & 03) << 1) | ((op >> 9) & 01); + reason = ch_op_start (ch, ea, FALSE); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00640: case 00641: case 00642: case 00643: /* SCHx */ + case 01640: case 01641: case 01642: case 01643: + if (prot_trap (0)) /* user mode? */ + break; + ch = ((op & 03) << 1) | ((op >> 9) & 01); + if ((reason = ch_op_store (ch, &SR)) == SCPE_OK) + Write (ea, SR); + break; + + case 00644: case 00645: case 00646: case 00647: /* SCDx */ + case 01644: case 01645: case 01646: case 01647: + ch = ((op & 03) << 1) | ((op >> 9) & 01); + if ((reason = ch_op_store_diag (ch, &SR)) == SCPE_OK) + Write (ea, SR); + break; + + case 00762: /* RDS */ + if (prot_trap (0) || sel_trap (PC)) + break; + ch = GET_U_CH (IR); + reason = ch_op_ds (ch, CHSL_RDS, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00764: /* BSR */ + if (prot_trap (0) || sel_trap (PC)) + break; + ch = GET_U_CH (IR); + reason = ch_op_nds (ch, CHSL_BSR, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00766: /* WRS */ + if (prot_trap (0) || sel_trap (PC)) + break; + ch = GET_U_CH (IR); + reason = ch_op_ds (ch, CHSL_WRS, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00770: /* WEF */ + if (prot_trap (0) || sel_trap (PC)) + break; + ch = GET_U_CH (IR); + reason = ch_op_nds (ch, CHSL_WEF, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00772: /* REW */ + if (prot_trap (0) || sel_trap (PC)) + break; + ch = GET_U_CH (IR); + reason = ch_op_nds (ch, CHSL_REW, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 01764: /* BSF */ + if (prot_trap (0) || sel_trap (PC)) + break; + ch = GET_U_CH (IR); + reason = ch_op_nds (ch, CHSL_BSF, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 01772: /* RUN */ + if (prot_trap (0) || sel_trap (PC)) + break; + ch = GET_U_CH (IR); + reason = ch_op_nds (ch, CHSL_RUN, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00776: /* SDN */ + if (prot_trap (0) || sel_trap (PC)) + break; + ch = GET_U_CH (IR); + reason = ch_op_nds (ch, CHSL_SDN, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + default: + if (stop_illop) + reason = STOP_ILLEG; + break; + } + } /* end else */ + + if (reason) { /* reason code? */ + if (reason == ERR_STALL) { /* stall? */ + PC = oldPC; /* back up PC */ + reason = 0; + } + else if (reason == STOP_HALT) { /* halt? wait for IO */ + t_stat r; + for (i = 0; (i < HALT_IO_LIMIT) && !ch_qidle (); i++) { + sim_interval = 0; + if (r = sim_process_event ()) /* process events */ + return r; + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + while (ch_req) { /* until no ch req */ + for (j = 0; j < NUM_CHAN; j++) { /* loop thru channels */ + if (ch_req & REQ_CH (j)) { /* channel request? */ + if (r = ch_proc (j)) + return r; + } + chtr_pend = chtr_eval (NULL); + } + } /* end while ch_req */ + } /* end for wait */ + if (chtr_pend) /* trap? cancel HALT */ + reason = 0; + } /* end if HALT */ + } /* end if reason */ + } /* end while */ + +pcq_r->qptr = pcq_p; /* update pc q ptr */ +return reason; +} + +/* Get index register for indexing */ + +uint32 get_xri (uint32 tag) +{ +tag = tag & INST_M_TAG; + +if (tag) { + if (mode_multi) { + uint32 r = 0; + if (tag & 1) + r = r | XR[1]; + if (tag & 2) + r = r | XR[2]; + if (tag & 4) + r = r | XR[4]; + return r & eamask; + } + return XR[tag] & eamask; + } +return 0; +} + +/* Get index register for instruction execution + + Instructions which are 'executing directly' on index registers rewrite + the index register value. In multi-tag mode, this causes all registers + involved in the OR function to receive the OR'd value. */ + +uint32 get_xrx (uint32 tag) +{ +tag = tag & INST_M_TAG; + +if (tag) { + if (mode_multi) { + uint32 r = 0; + if (tag & 1) + r = r | XR[1]; + if (tag & 2) + r = r | XR[2]; + if (tag & 4) + r = r | XR[4]; + put_xr (tag, r); + return r & eamask; + } + return XR[tag] & eamask; + } +return 0; +} + +/* Store index register */ + +void put_xr (uint32 tag, uint32 dat) +{ +tag = tag & INST_M_TAG; +dat = dat & eamask; + +if (tag) { + if (mode_multi) { + if (tag & 1) + XR[1] = dat; + if (tag & 2) + XR[2] = dat; + if (tag & 4) + XR[4] = dat; + } + else XR[tag] = dat; + } +return; +} + +/* Floating point trap */ + +t_bool fp_trap (uint32 spill) +{ +if (mode_ftrap) { + WriteTAD (TRAP_STD_SAV, PC, spill); + PCQ_ENTRY; + PC = TRAP_FP_PC; + return TRUE; + } +else { + if (spill & TRAP_F_AC) + ind_ovf = 1; + if (spill & TRAP_F_MQ) + ind_mqo = 1; + } +return FALSE; +} + +/* (CTSS) Protection trap */ + +t_bool prot_trap (uint32 decr) +{ +if (mode_user) { + WriteTAD (TRAP_PROT_SAV, PC, decr); + PCQ_ENTRY; + PC = TRAP_PROT_PC; + return TRUE; + } +return FALSE; +} + +/* Store trap address and decrement, with A/B select flags; clear A/B, user mode */ + +void WriteTAD (uint32 pa, uint32 addr, uint32 decr) +{ +t_uint64 mem; + +if (inst_base) + decr |= TRAP_F_BINST; +if (data_base) + decr |= TRAP_F_BDATA; +mem = ReadP (pa) & ~(XMASK | AMASK); +mem |= (((t_uint64) (decr & AMASK)) << INST_V_DEC) | + ((t_uint64) (addr & AMASK)); +WriteP (pa, mem); +mode_ctrap = 0; +mode_strap = 0; +mode_storn = 0; +mode_user = 0; +inst_base = 0; +data_base = 0; +return; +} + +/* Copy trap */ + +t_bool cpy_trap (uint32 va) +{ +if (mode_ctrap) { + WriteTA (TRAP_704_SAV, va); + PCQ_ENTRY; + TrapXfr (TRAP_CPY_PC); + return TRUE; + } +return FALSE; +} + +/* Select trap */ + +t_bool sel_trap (uint32 va) +{ +if (mode_strap) { + WriteTA (TRAP_704_SAV, va); + PCQ_ENTRY; + TrapXfr (TRAP_SEL_PC); + return TRUE; + } +return FALSE; +} + +/* Store trap address - do not alter state yet (might be TRA) */ + +void WriteTA (uint32 pa, uint32 dat) +{ +t_uint64 mem; + +mem = ReadP (pa) & ~AMASK; +mem |= (dat & AMASK); +WriteP (pa, mem); +return; +} + +/* Set trap PC - second half of address-only trap */ + +void TrapXfr (uint32 newpc) +{ +PC = newpc; +mode_ctrap = 0; +mode_strap = 0; +mode_storn = 0; +mode_user = 0; +inst_base = 0; +data_base = 0; +return; +} + +/* Read instruction and indirect */ + +t_bool ReadI (uint32 va, t_uint64 *val) +{ +if (mode_user) { + va = (va + ind_reloc) & AMASK; + if ((va < ind_start) || (va > ind_limit)) { + prot_trap (0); + return FALSE; + } + } +*val = M[va | inst_base]; +return TRUE; +} + +/* Read */ + +t_bool Read (uint32 va, t_uint64 *val) +{ +if (mode_user) { + va = (va + ind_reloc) & AMASK; + if ((va < ind_start) || (va > ind_limit)) { + prot_trap (0); + return FALSE; + } + } +*val = M[va | data_base]; +return TRUE; +} + +/* Write */ + +t_bool Write (uint32 va, t_uint64 dat) +{ +if (mode_user) { + va = (va + ind_reloc) & AMASK; + if ((va < ind_start) || (va > ind_limit)) { + prot_trap (0); + return FALSE; + } + } +M[va | data_base] = dat; +return TRUE; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +ind_ovf = 0; +ind_mqo = 0; +ind_dvc = 0; +ind_ioc = 0; +ind_reloc = 0; +ind_start = 0; +ind_limit = 0; +mode_ttrap = 0; +mode_ctrap = 0; +mode_strap = 0; +mode_ftrap = 1; +mode_storn = 0; +if (cpu_model & (I_94|I_CT)) + mode_multi = 0; +else mode_multi = 1; +mode_user = 0; +inst_base = 0; +data_base = 0; +ch_req = 0; +chtr_pend = chtr_enab = 0; +chtr_inht = chtr_inhi = 0; +ht_pend = 0; +SLT = 0; +XR[0] = 0; +if (M == NULL) + M = (t_uint64 *) calloc (MAXMEMSIZE, sizeof (t_uint64)); +if (M == NULL) + return SCPE_MEM; +pcq_r = find_reg ("PCQ", NULL, dptr); +if (pcq_r) + pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr ea, UNIT *uptr, int32 sw) +{ +if (vptr == NULL) + return SCPE_ARG; +if ((sw & (SWMASK ('A') | SWMASK ('B')))? (ea > AMASK): (ea >= MEMSIZE)) + return SCPE_NXM; +if ((sw & SWMASK ('B')) || + ((sw & SWMASK ('V')) && mode_user && inst_base)) + ea = ea | BCORE_BASE; +*vptr = M[ea] & DMASK; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr ea, UNIT *uptr, int32 sw) +{ +if ((sw & (SWMASK ('A') | SWMASK ('B')))? (ea > AMASK): (ea >= MEMSIZE)) + return SCPE_NXM; +if (sw & SWMASK ('B')) + ea = ea | BCORE_BASE; +M[ea] = val & DMASK; +return SCPE_OK; +} + +/* Set model */ + +t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +UNIT *chuptr = mt_dev[CHRONO_CH].units + CHRONO_UNIT; +extern DEVICE clk_dev; + +cpu_model = val; +if (val & I_CT) { + uptr->capac = MAXMEMSIZE; + detach_unit (uptr); + chuptr->flags &= ~UNIT_ATTABLE; + clk_dev.flags &= ~DEV_DIS; + } +else { + uptr->capac = STDMEMSIZE; + chuptr->flags |= UNIT_ATTABLE; + } +if (!(cpu_model & I_94)) + mode_multi = 1; +return SCPE_OK; +} + +/* Show CTSS */ + +t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (cpu_model & I_CT) + fputs ("CTSS", st); +else if (cpu_model & I_94) + fputs ("7094", st); +else fputs ("7090", st); +return SCPE_OK; +} + +/* Insert history entry */ + +static uint32 inst_io_tab[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0000 - 0377 */ + 0, 0, 0, 0x000000FF, 0, 0, 0, 0x45540000, /* 0400 - 0777 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 1000 - 1400 */ + 0, 0, 0, 0x000000FF, 0, 0, 0, 0 /* 1400 - 1777 */ + }; + +void cpu_ent_hist (uint32 pc, uint32 ea, t_uint64 ir, t_uint64 opnd) +{ +int32 prv_p; + +if (pc & HIST_PC) { + if ((pc == hst[hst_p].pc) && (ir == hst[hst_p].ir)) { /* repeat last? */ + hst[hst_p].rpt++; + return; + } + prv_p = hst_p? hst_p - 1: hst_lnt - 1; + if ((pc == hst[prv_p].pc) && (ir == hst[prv_p].ir)) { /* 2 line loop? */ + hst[prv_p].rpt++; + return; + } + if (hst_ch & HIST_CH_I) { /* IO only? */ + uint32 op = GET_OPC (ir); /* get opcode */ + if ((ir & INST_T_DEC) || + !(inst_io_tab[op / 32] & (1u << (op & 037)))) + return; + } + } +hst_p = (hst_p + 1); /* next entry */ +if (hst_p >= hst_lnt) + hst_p = 0; +hst[hst_p].pc = pc; +hst[hst_p].ir = ir; +hst[hst_p].ac = AC; +hst[hst_p].mq = MQ; +hst[hst_p].si = SI; +hst[hst_p].ea = ea; +hst[hst_p].opnd = opnd; +hst[hst_p].rpt = 0; +return; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) + hst[i].pc = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) + return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = hst_ch = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) + return SCPE_MEM; + hst_lnt = lnt; + if (sim_switches & SWMASK ('I')) + hst_ch = HIST_CH_I|HIST_CH_C; + else if (sim_switches & SWMASK ('C')) + hst_ch = HIST_CH_C; + else hst_ch = 0; + } +return SCPE_OK; +} + +/* Print one instruction */ + +t_stat cpu_fprint_one_inst (FILE *st, uint32 pc, uint32 rpt, uint32 ea, + t_uint64 ir, t_uint64 ac, t_uint64 mq, t_uint64 si, t_uint64 opnd) +{ +int32 ch; +t_value sim_eval; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +sim_eval = ir; +if (pc & HIST_PC) { /* instruction? */ + fputs ("CPU ", st); + fprintf (st, "%05o ", pc & AMASK); + if (rpt == 0) + fprintf (st, " "); + else if (rpt < 1000000) + fprintf (st, "%6d ", rpt); + else fprintf (st, "%5dM ", rpt / 1000000); + fprint_val (st, ac, 8, 38, PV_RZRO); + fputc (' ', st); + fprint_val (st, mq, 8, 36, PV_RZRO); + fputc (' ', st); + fprint_val (st, si, 8, 36, PV_RZRO); + fputc (' ', st); + if (ir & INST_T_DEC) + fprintf (st, " "); + else fprintf (st, "%05o ", ea); + if (fprint_sym (st, pc & AMASK, &sim_eval, &cpu_unit, SWMASK ('M')) > 0) { + fputs ("(undefined) ", st); + fprint_val (st, ir, 8, 36, PV_RZRO); + } + else if (!(ir & INST_T_DEC) && (op_flags[GET_OPC (ir)] & I_R)) { + fputs (" [", st); + fprint_val (st, opnd, 8, 36, PV_RZRO); + fputc (']', st); + } + fputc ('\n', st); /* end line */ + } /* end if instruction */ +else if (ch = HIST_CH (pc)) { /* channel? */ + fprintf (st, "CH%c ", 'A' + ch - 1); + fprintf (st, "%05o ", pc & AMASK); + fputs (" ", st); + fprintf (st, "%05o ", ea & AMASK); + if (fprint_sym (st, pc & AMASK, &sim_eval, &cpu_unit, + (ch_dev[ch - 1].flags & DEV_7909)? SWMASK ('N'): SWMASK ('I')) > 0) { + fputs ("(undefined) ", st); + fprint_val (st, ir, 8, 36, PV_RZRO); + } + fputc ('\n', st); /* end line */ + } /* end else channel */ +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 k, di, lnt; +char *cptr = (char *) desc; +t_stat r; +InstHistory *h; + +if (hst_lnt == 0) /* enabled? */ + return SCPE_NOFNC; +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) + return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) + di = di + hst_lnt; +fprintf (st, " PC repeat AC MQ SI EA IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + cpu_fprint_one_inst (st, h->pc, h->rpt, h->ea, h->ir, h->ac, h->mq, h->si, h->opnd); + } /* end for */ +return SCPE_OK; +} diff --git a/I7094/i7094_defs.h b/I7094/i7094_defs.h index 633a75c7..929f8b73 100644 --- a/I7094/i7094_defs.h +++ b/I7094/i7094_defs.h @@ -1,6 +1,6 @@ /* i7094_defs.h: IBM 7094 simulator definitions - Copyright (c) 2003-2008, Robert M Supnik + Copyright (c) 2003-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,13 +26,21 @@ This simulator incorporates prior work by Paul Pierce, Dave Pitts, and Rob Storey. Tom Van Vleck, Stan Dunten, Jerry Saltzer, and other CTSS veterans helped to reconstruct the CTSS hardware RPQ's. Dave Pitts gets special - thanks for patiently coaching me through IBSYS debug. */ + thanks for patiently coaching me through IBSYS debug. + + 22-May-10 RMS Added check for 64b addresses + +*/ #ifndef _I7094_DEFS_H_ #define _I7094_DEFS_H_ 0 #include "sim_defs.h" /* simulator defns */ +#if defined(USE_ADDR64) +#error "7094 does not support 64b addresses!" +#endif + /* Simulator stop codes */ #define STOP_HALT 1 /* halted */ @@ -168,6 +176,8 @@ typedef struct { #define INST_M_TAG 07 #define INST_V_ADDR 0 #define INST_M_ADDR 077777 +#define INST_V_4B 0 +#define INST_M_4B 017 #define GET_OPD(x) ((uint32) (((x) >> INST_V_OPD) & INST_M_OPD)) #define GET_DEC(x) ((uint32) (((x) >> INST_V_DEC) & INST_M_DEC)) diff --git a/I7094/i7094_mt.c b/I7094/i7094_mt.c index 6c8f9cf4..18c34942 100644 --- a/I7094/i7094_mt.c +++ b/I7094/i7094_mt.c @@ -24,6 +24,8 @@ in this Software without prior written authorization from Robert M Supnik. mt magtape simulator + + 16-Jul-10 RMS Fixed handling of BSR, BSF (from Dave Pitts) */ #include "i7094_defs.h" @@ -44,7 +46,7 @@ uint32 mt_bptr[NUM_CHAN]; uint32 mt_blnt[NUM_CHAN]; t_uint64 mt_chob[NUM_CHAN]; uint32 mt_chob_v[NUM_CHAN]; -uint32 mt_tshort = 2; +uint32 mt_tshort = 2; /* "a few microseconds" */ uint32 mt_twef = 25000; /* 50 msec */ uint32 mt_tstart = 29000; /* 58 msec */ uint32 mt_tstop = 10000; /* 20 msec */ @@ -474,8 +476,6 @@ else { /* real tape */ case CHSL_RDS: case CHSL_WRS: - case CHSL_BSR: - case CHSL_BSF: /* rd, wr, backspace */ sim_activate (uptr, mt_tstart); /* schedule op */ break; @@ -483,10 +483,10 @@ else { /* real tape */ sim_activate (uptr, mt_twef); /* schedule op */ break; - case CHSL_RUN: - sim_activate (uptr, mt_tshort); /* schedule quick event */ - break; + case CHSL_BSR: + case CHSL_BSF: /* backspace */ case CHSL_REW: + case CHSL_RUN: case CHSL_SDN: /* rew, rew/unl, set det */ sim_activate (uptr, mt_tshort); /* schedule quick event */ break; @@ -537,7 +537,7 @@ if (uptr->UST == (CHSL_WRS|CHSL_2ND)) { /* data write? */ xb[mt_bptr[ch]++] = by; /* put in buffer */ } if (eorfl) - return mt_rec_end (uptr); /* EOR? write rec */ + return mt_rec_end (uptr); /* EOR? write rec */ return SCPE_OK; } return SCPE_IERR; @@ -649,10 +649,15 @@ switch (uptr->UST) { /* case on state */ mt_unit[ch]? "continuing": "disconnecting"); return SCPE_OK; - case CHSL_BSR: /* backspace rec */ + case CHSL_BSR: case CHSL_BSF: /* backspace */ + uptr->UST = uptr->UST | CHSL_2ND; /* set 2nd state */ + sim_activate (uptr, mt_tstart); /* reactivate */ + ch6_end_nds (ch); /* disconnect */ + return SCPE_OK; + + case CHSL_BSR|CHSL_2ND: /* backspace rec */ r = sim_tape_sprecr (uptr, &bc); /* space backwards */ mt_unit[ch] = 0; /* clr ctrl busy */ - ch6_end_nds (ch); /* disconnect */ if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, ">>%s%d BSR complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); @@ -660,10 +665,9 @@ switch (uptr->UST) { /* case on state */ return SCPE_OK; return mt_map_err (uptr, r); - case CHSL_BSF: /* backspace file */ + case CHSL_BSF|CHSL_2ND: /* backspace file */ while ((r = sim_tape_sprecr (uptr, &bc)) == MTSE_OK) ; mt_unit[ch] = 0; /* clr ctrl busy */ - ch6_end_nds (ch); /* disconnect */ if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, ">>%s%d BSF complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); diff --git a/I7094/i7094_mt_old.c b/I7094/i7094_mt_old.c new file mode 100644 index 00000000..6c8f9cf4 --- /dev/null +++ b/I7094/i7094_mt_old.c @@ -0,0 +1,861 @@ +/* i7094_mt.c: IBM 7094 magnetic tape simulator + + Copyright (c) 2003-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mt magtape simulator +*/ + +#include "i7094_defs.h" +#include "sim_tape.h" + +#define UST u3 /* unit state */ +#define UCH u4 /* channel number */ +#define MTUF_V_LDN (MTUF_V_UF + 0) +#define MTUF_LDN (1 << MTUF_V_LDN) +#define MT_MAXFR ((1 << 18) + 2) + +#define QCHRONO(c,u) ((cpu_model & I_CT) && \ + ((c) == CHRONO_CH) && ((u) == CHRONO_UNIT)) + +uint8 *mtxb[NUM_CHAN] = { NULL }; /* xfer buffer */ +uint32 mt_unit[NUM_CHAN]; /* unit */ +uint32 mt_bptr[NUM_CHAN]; +uint32 mt_blnt[NUM_CHAN]; +t_uint64 mt_chob[NUM_CHAN]; +uint32 mt_chob_v[NUM_CHAN]; +uint32 mt_tshort = 2; +uint32 mt_twef = 25000; /* 50 msec */ +uint32 mt_tstart = 29000; /* 58 msec */ +uint32 mt_tstop = 10000; /* 20 msec */ +uint32 mt_tword = 50; /* 125 usec */ + +static const uint8 odd_par[64] = { + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1 + }; + +static const char *tape_stat[] = { + "OK", "TMK", "UNATT", "IOERR", "INVRECLNT", + "FMT", "BOT", "EOM", "RECERR", "WRPROT" + }; + +extern uint32 PC; +extern uint32 cpu_model; +extern uint32 ind_ioc; +extern FILE *sim_deb; +extern char *sel_name[]; + +t_stat mt_chsel (uint32 ch, uint32 sel, uint32 unit); +t_stat mt_chwr (uint32 ch, t_uint64 val, uint32 flags); +t_stat mt_rec_end (UNIT *uptr); +t_stat mt_svc (UNIT *uptr); +t_stat mt_reset (DEVICE *dptr); +t_stat mt_attach (UNIT *uptr, char *cptr); +t_stat mt_boot (int32 unitno, DEVICE *dptr); +t_stat mt_map_err (UNIT *uptr, t_stat st); + +extern uint32 chrono_rd (uint8 *buf, uint32 bufsiz); + +/* MT data structures + + mt_dev MT device descriptor + mt_unit MT unit list + mt_reg MT register list + mt_mod MT modifier list +*/ + +DIB mt_dib = { &mt_chsel, &mt_chwr }; + +MTAB mt_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTUF_LDN, 0, "high density", "HIGH", NULL }, + { MTUF_LDN, MTUF_LDN, "low density", "LOW", NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { 0 } + }; + +UNIT mta_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mta_reg[] = { + { ORDATA (UNIT, mt_unit[0], 5) }, + { ORDATA (CHOB, mt_chob[0], 36) }, + { FLDATA (CHOBV, mt_chob_v[0], 0) }, + { DRDATA (BPTR, mt_bptr[0], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[0], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mta_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mta_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mtb_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mtb_reg[] = { + { ORDATA (UNIT, mt_unit[1], 5) }, + { ORDATA (CHOB, mt_chob[1], 36) }, + { FLDATA (CHOBV, mt_chob_v[1], 0) }, + { DRDATA (BPTR, mt_bptr[1], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[1], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mtb_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mtb_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mtc_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mtc_reg[] = { + { ORDATA (UNIT, mt_unit[2], 5) }, + { ORDATA (CHOB, mt_chob[2], 36) }, + { FLDATA (CHOBV, mt_chob_v[2], 0) }, + { DRDATA (BPTR, mt_bptr[2], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[2], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mtc_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mtc_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mtd_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mtd_reg[] = { + { ORDATA (UNIT, mt_unit[3], 5) }, + { ORDATA (CHOB, mt_chob[3], 36) }, + { FLDATA (CHOBV, mt_chob_v[3], 0) }, + { DRDATA (BPTR, mt_bptr[3], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[3], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mtd_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mtd_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mte_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mte_reg[] = { + { ORDATA (UNIT, mt_unit[4], 5) }, + { ORDATA (CHOB, mt_chob[4], 36) }, + { FLDATA (CHOBV, mt_chob_v[4], 0) }, + { DRDATA (BPTR, mt_bptr[4], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[4], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mte_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mte_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mtf_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mtf_reg[] = { + { ORDATA (UNIT, mt_unit[5], 5) }, + { ORDATA (CHOB, mt_chob[5], 36) }, + { FLDATA (CHOBV, mt_chob_v[5], 0) }, + { DRDATA (BPTR, mt_bptr[5], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[5], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mtf_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mtf_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mtg_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mtg_reg[] = { + { ORDATA (UNIT, mt_unit[6], 5) }, + { ORDATA (CHOB, mt_chob[6], 36) }, + { FLDATA (CHOBV, mt_chob_v[6], 0) }, + { DRDATA (BPTR, mt_bptr[6], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[6], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mtg_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mtg_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mth_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mth_reg[] = { + { ORDATA (UNIT, mt_unit[7], 5) }, + { ORDATA (CHOB, mt_chob[7], 36) }, + { FLDATA (CHOBV, mt_chob_v[7], 0) }, + { DRDATA (BPTR, mt_bptr[7], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[7], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mth_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mth_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +DEVICE mt_dev[NUM_CHAN] = { + { + "MTA", mta_unit, mta_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + &mt_boot, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DEBUG + }, + { + "MTB", mtb_unit, mtb_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + }, + { + "MTC", mtc_unit, mtc_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + }, + { + "MTD", mtd_unit, mtd_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + }, + { + "MTE", mte_unit, mte_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + }, + { + "MTF", mtf_unit, mtf_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + }, + { + "MTG", mtg_unit, mtg_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + }, + { + "MTH", mth_unit, mth_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + } + }; + +/* Select controller + + Inputs: + ch = channel + cmd = select command + unit = unit + Outputs: + status = SCPE_OK if ok + STOP_STALL if busy + error code if error +*/ + +static const int mt_must_att[CHSL_NUM] = { + 0, 1, 1, 0, 1, 1, 0, 0, + 1, 1, 1, 1, 1, 1, 0, 0 + }; + +static const int mt_will_wrt[CHSL_NUM] = { + 0, 0, 1, 0, 0, 1, 0, 0, + 1, 1, 0, 0, 0, 0, 0, 0 + }; + +t_stat mt_chsel (uint32 ch, uint32 cmd, uint32 unit) +{ +UNIT *uptr; +uint32 u = unit & 017; + +if ((ch >= NUM_CHAN) || (cmd == 0) || (cmd >= CHSL_NUM)) + return SCPE_IERR; /* invalid arg? */ +if (mt_dev[ch].flags & DEV_DIS) /* disabled? */ + return STOP_NXDEV; +if ((u == 0) || (u > MT_NUMDR)) /* valid unit? */ + return STOP_NXDEV; +uptr = mt_dev[ch].units + u; /* get unit ptr */ +if (uptr->flags & UNIT_DIS) /* disabled? */ + return STOP_NXDEV; +if (mt_unit[ch] || sim_is_active (uptr)) /* ctrl or unit busy? */ + return ERR_STALL; /* stall */ +if (QCHRONO (ch, u)) { /* Chronolog clock? */ + if (cmd != CHSL_RDS) /* only reads */ + return STOP_ILLIOP; + sim_activate (uptr, mt_tword); /* responds quickly */ + } +else { /* real tape */ + if (!(uptr->flags & UNIT_ATT) && mt_must_att[cmd]) /* unit unatt? */ + return SCPE_UNATT; + if (sim_tape_wrp (uptr) && mt_will_wrt[cmd]) /* unit wrp && write? */ + return STOP_WRP; + if (DEBUG_PRS (mt_dev[ch])) + fprintf (sim_deb, ">>%s%d %s, pos = %d\n", + mt_dev[ch].name, u, sel_name[cmd], uptr->pos); + + switch (cmd) { /* case on cmd */ + + case CHSL_RDS: + case CHSL_WRS: + case CHSL_BSR: + case CHSL_BSF: /* rd, wr, backspace */ + sim_activate (uptr, mt_tstart); /* schedule op */ + break; + + case CHSL_WEF: /* write eof? */ + sim_activate (uptr, mt_twef); /* schedule op */ + break; + + case CHSL_RUN: + sim_activate (uptr, mt_tshort); /* schedule quick event */ + break; + case CHSL_REW: + case CHSL_SDN: /* rew, rew/unl, set det */ + sim_activate (uptr, mt_tshort); /* schedule quick event */ + break; + + default: + return SCPE_IERR; + } /* end switch */ + } /* end else */ + +uptr->UST = cmd; /* set cmd */ +mt_unit[ch] = unit & 0777; /* save unit */ +return SCPE_OK; +} + +/* Channel write routine */ + +t_stat mt_chwr (uint32 ch, t_uint64 val, uint32 eorfl) +{ +int32 k, u; +uint8 by, *xb; +UNIT *uptr; + +if (ch >= NUM_CHAN) /* invalid chan? */ + return SCPE_IERR; +xb = mtxb[ch]; /* get xfer buf */ +u = mt_unit[ch] & 017; +if ((xb == NULL) || (u > MT_NUMDR)) /* invalid args? */ + return SCPE_IERR; +uptr = mt_dev[ch].units + u; /* get unit */ +mt_chob[ch] = val & DMASK; /* save word from chan */ +mt_chob_v[ch] = 1; /* set valid */ + +if (uptr->UST == (CHSL_WRS|CHSL_2ND)) { /* data write? */ + for (k = 30; /* proc 6 bytes */ + (k >= 0) && (mt_bptr[ch] < MT_MAXFR); + k = k - 6) { + by = (uint8) ((val >> k) & 077); /* get byte */ + if ((mt_unit[ch] & 020) == 0) { /* BCD? */ + if (by == 0) /* cvt bin 0 */ + by = BCD_ZERO; + else if (by & 020) /* invert zones */ + by = by ^ 040; + if (!odd_par[by]) /* even parity */ + by = by | 0100; + } + else if (odd_par[by]) /* bin, odd par */ + by = by | 0100; + xb[mt_bptr[ch]++] = by; /* put in buffer */ + } + if (eorfl) + return mt_rec_end (uptr); /* EOR? write rec */ + return SCPE_OK; + } +return SCPE_IERR; +} + +/* Unit timeout */ + +t_stat mt_svc (UNIT *uptr) +{ +uint32 i, u, ch = uptr->UCH; /* get channel number */ +uint8 by, *xb = mtxb[ch]; /* get xfer buffer */ +t_uint64 dat; +t_mtrlnt bc; +t_stat r; + +if (xb == NULL) /* valid buffer? */ + return SCPE_IERR; +u = uptr - mt_dev[ch].units; +switch (uptr->UST) { /* case on state */ + + case CHSL_RDS: /* read start */ + if (QCHRONO (ch, mt_unit[ch] & 017)) /* Chronolog clock? */ + bc = chrono_rd (xb, MT_MAXFR); /* read clock */ + else { /* real tape */ + r = sim_tape_rdrecf (uptr, xb, &bc, MT_MAXFR); /* read record */ + if (r = mt_map_err (uptr, r)) /* map status */ + return r; + if (mt_unit[ch] == 0) /* disconnected? */ + return SCPE_OK; + } /* end else Chrono */ + if (!ch6_qconn (ch, mt_unit[ch])) { /* chan disconnected? */ + mt_unit[ch] = 0; /* clr ctrl busy */ + return SCPE_OK; + } + for (i = bc; i < (bc + 6); i++) /* extra 0's */ + xb[i] = 0; + mt_bptr[ch] = 0; /* set ptr, lnt */ + mt_blnt[ch] = bc; + uptr->UST = CHSL_RDS|CHSL_2ND; /* next state */ + sim_activate (uptr, mt_tword); + break; + + case CHSL_RDS|CHSL_2ND: /* read word */ + for (i = 0, dat = 0; i < 6; i++) { /* proc 6 bytes */ + by = xb[mt_bptr[ch]++] & 077; /* get next byte */ + if ((mt_unit[ch] & 020) == 0) { /* BCD? */ + if (by == BCD_ZERO) /* cvt BCD 0 */ + by = 0; + else if (by & 020) /* invert zones */ + by = by ^ 040; + } + dat = (dat << 6) | ((t_uint64) by); + } + if (mt_bptr[ch] >= mt_blnt[ch]) { /* end of record? */ + ch6_req_rd (ch, mt_unit[ch], dat, CH6DF_EOR); + uptr->UST = CHSL_RDS|CHSL_3RD; /* next state */ + sim_activate (uptr, mt_tstop); /* long timing */ + } + else { + ch6_req_rd (ch, mt_unit[ch], dat, 0); /* send to channel */ + sim_activate (uptr, mt_tword); /* next word */ + } + break; + + case CHSL_RDS|CHSL_3RD: /* end record */ + if (ch6_qconn (ch, mt_unit[ch])) { /* ch still conn? */ + uptr->UST = CHSL_RDS; /* initial state */ + sim_activate (uptr, mt_tshort); /* sched next record */ + } + else mt_unit[ch] = 0; /* clr ctrl busy */ + if (DEBUG_PRS (mt_dev[ch])) + fprintf (sim_deb, ">>%s%d RDS complete, pos = %d, %s\n", + mt_dev[ch].name, u, uptr->pos, + mt_unit[ch]? "continuing": "disconnecting"); + return SCPE_OK; + + case CHSL_WRS: /* write start */ + if (!ch6_qconn (ch, mt_unit[ch])) { /* chan disconnected? */ + mt_unit[ch] = 0; /* clr ctrl busy */ + return SCPE_OK; /* (writes blank tape) */ + } + mt_bptr[ch] = 0; /* init buffer */ + uptr->UST = CHSL_WRS|CHSL_2ND; /* next state */ + ch6_req_wr (ch, mt_unit[ch]); /* request channel */ + mt_chob[ch] = 0; /* clr, inval buffer */ + mt_chob_v[ch] = 0; + sim_activate (uptr, mt_tword); /* wait for word */ + break; + + case CHSL_WRS|CHSL_2ND: /* write word */ + if (!ch6_qconn (ch, mt_unit[ch])) /* disconnected? */ + return mt_rec_end (uptr); /* write record */ + if (mt_chob_v[ch]) /* valid? clear */ + mt_chob_v[ch] = 0; + else ind_ioc = 1; /* no, io check */ + ch6_req_wr (ch, mt_unit[ch]); /* request channel */ + sim_activate (uptr, mt_tword); /* next word */ + break; + + case CHSL_WRS|CHSL_3RD: /* write stop */ + if (ch6_qconn (ch, mt_unit[ch])) { /* chan active? */ + uptr->UST = CHSL_WRS; /* initial state */ + sim_activate (uptr, mt_tshort); /* sched next record */ + } + else mt_unit[ch] = 0; /* clr ctrl busy */ + if (DEBUG_PRS (mt_dev[ch])) + fprintf (sim_deb, ">>%s%d WRS complete, pos = %d, %s\n", + mt_dev[ch].name, u, uptr->pos, + mt_unit[ch]? "continuing": "disconnecting"); + return SCPE_OK; + + case CHSL_BSR: /* backspace rec */ + r = sim_tape_sprecr (uptr, &bc); /* space backwards */ + mt_unit[ch] = 0; /* clr ctrl busy */ + ch6_end_nds (ch); /* disconnect */ + if (DEBUG_PRS (mt_dev[ch])) + fprintf (sim_deb, ">>%s%d BSR complete, pos = %d\n", + mt_dev[ch].name, u, uptr->pos); + if (r == MTSE_TMK) /* allow tape mark */ + return SCPE_OK; + return mt_map_err (uptr, r); + + case CHSL_BSF: /* backspace file */ + while ((r = sim_tape_sprecr (uptr, &bc)) == MTSE_OK) ; + mt_unit[ch] = 0; /* clr ctrl busy */ + ch6_end_nds (ch); /* disconnect */ + if (DEBUG_PRS (mt_dev[ch])) + fprintf (sim_deb, ">>%s%d BSF complete, pos = %d\n", + mt_dev[ch].name, u, uptr->pos); + if (r == MTSE_TMK) /* allow tape mark */ + return SCPE_OK; + return mt_map_err (uptr, r); /* map others */ + + case CHSL_WEF: /* write eof */ + r = sim_tape_wrtmk (uptr); /* write tape mark */ + mt_unit[ch] = 0; /* clr ctrl busy */ + ch6_end_nds (ch); /* disconnect */ + if (DEBUG_PRS (mt_dev[ch])) + fprintf (sim_deb, ">>%s%d WEF complete, pos = %d\n", + mt_dev[ch].name, u, uptr->pos); + return mt_map_err (uptr, r); + + case CHSL_REW: case CHSL_RUN: /* rewind, unload */ + uptr->UST = uptr->UST | CHSL_2ND; /* set 2nd state */ + sim_activate (uptr, mt_tstart); /* reactivate */ + mt_unit[ch] = 0; /* clr ctrl busy */ + ch6_end_nds (ch); /* disconnect */ + return SCPE_OK; + + case CHSL_REW | CHSL_2ND: + sim_tape_rewind (uptr); + if (DEBUG_PRS (mt_dev[ch])) + fprintf (sim_deb, ">>%s%d REW complete, pos = %d\n", + mt_dev[ch].name, u, uptr->pos); + return SCPE_OK; + + case CHSL_RUN | CHSL_2ND: + sim_tape_detach (uptr); + if (DEBUG_PRS (mt_dev[ch])) + fprintf (sim_deb, ">>%s%d RUN complete, pos = %d\n", + mt_dev[ch].name, u, uptr->pos); + return SCPE_OK; + + case CHSL_SDN: + if (mt_unit[ch] & 020) /* set density flag */ + uptr->flags = uptr-> flags & ~MTUF_LDN; + else uptr->flags = uptr->flags | MTUF_LDN; + mt_unit[ch] = 0; /* clr ctrl busy */ + ch6_end_nds (ch); /* disconnect */ + if (DEBUG_PRS (mt_dev[ch])) + fprintf (sim_deb, ">>%s%d SDN complete, pos = %d\n", + mt_dev[ch].name, u, uptr->pos); + return SCPE_OK; + + default: + return SCPE_IERR; + } + +return SCPE_OK; +} + +/* End record routine */ + +t_stat mt_rec_end (UNIT *uptr) +{ +uint32 ch = uptr->UCH; +uint8 *xb = mtxb[ch]; +t_stat r; + +if (mt_bptr[ch]) { /* any data? */ + if (xb == NULL) + return SCPE_IERR; + r = sim_tape_wrrecf (uptr, xb, mt_bptr[ch]); /* write record */ + if (r = mt_map_err (uptr, r)) /* map error */ + return r; + } +uptr->UST = CHSL_WRS|CHSL_3RD; /* next state */ +sim_cancel (uptr); /* cancel current */ +sim_activate (uptr, mt_tstop); /* long timing */ +return SCPE_OK; +} + +/* Map tape error status */ + +t_stat mt_map_err (UNIT *uptr, t_stat st) +{ +uint32 ch = uptr->UCH; +uint32 u = mt_unit[ch]; +uint32 up = uptr - mt_dev[ch].units; + +if ((st != MTSE_OK) && DEBUG_PRS (mt_dev[ch])) + fprintf (sim_deb, ">>%s%d status = %s, pos = %d\n", + mt_dev[ch].name, up, tape_stat[st], uptr->pos); + +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* not attached */ + ch6_err_disc (ch, u, CHF_TRC); + mt_unit[ch] = 0; /* disconnect */ + return SCPE_IERR; + + case MTSE_IOERR: /* IO error */ + ch6_err_disc (ch, u, CHF_TRC); + mt_unit[ch] = 0; /* disconnect */ + return SCPE_IOERR; + + case MTSE_INVRL: /* invalid rec lnt */ + ch6_err_disc (ch, u, CHF_TRC); + mt_unit[ch] = 0; /* disconnect */ + return SCPE_MTRLNT; + + case MTSE_WRP: /* write protect */ + ch6_err_disc (ch, u, 0); + mt_unit[ch] = 0; /* disconnect */ + return STOP_WRP; + + case MTSE_EOM: /* end of medium */ + case MTSE_TMK: /* tape mark */ + ch6_err_disc (ch, u, CHF_EOF); + mt_unit[ch] = 0; /* disconnect */ + break; + + case MTSE_RECE: /* record in error */ + ch6_set_flags (ch, u, CHF_TRC); + break; + + case MTSE_BOT: /* reverse into BOT */ + ch6_set_flags (ch, u, CHF_BOT); + break; + + case MTSE_OK: /* no error */ + break; + } + +return SCPE_OK; +} + +/* Magtape reset */ + +t_stat mt_reset (DEVICE *dptr) +{ +uint32 ch = dptr - &mt_dev[0]; +uint32 j; +REG *rptr; +UNIT *uptr; + +if (mtxb[ch] == NULL) + mtxb[ch] = (uint8 *) calloc (MT_MAXFR + 6, sizeof (uint8)); +if (mtxb[ch] == NULL) /* allocate buffer */ + return SCPE_MEM; +rptr = find_reg ("BUF", NULL, dptr); /* update reg ptr */ +if (rptr == NULL) + return SCPE_IERR; +rptr->loc = (void *) mtxb[ch]; +mt_unit[ch] = 0; /* clear busy */ +mt_bptr[ch] = 0; /* clear buf ptrs */ +mt_blnt[ch] = 0; +mt_chob[ch] = 0; +mt_chob_v[ch] = 0; +for (j = 1; j <= MT_NUMDR; j++) { /* for all units */ + uptr = dptr->units + j; + uptr->UST = 0; /* clear state */ + uptr->UCH = ch; + sim_cancel (uptr); /* stop activity */ + } /* end for */ +return SCPE_OK; /* done */ +} + +/* Magtape attach */ + +t_stat mt_attach (UNIT *uptr, char *cptr) +{ +uptr->flags = uptr->flags & ~MTUF_LDN; /* start as hi den */ +return sim_tape_attach (uptr, cptr); +} + +/* Magtape boot */ + +#define BOOT_START 01000 + +static const t_uint64 boot_rom[5] = { + 0076200000000 + U_MTBIN - 1, /* RDS MT_binary */ + 0054000000000 + BOOT_START + 4, /* RCHA *+3 */ + 0054400000000, /* LCHA 0 */ + 0002100000001, /* TTR 1 */ + 0500003000000, /* IOCT 0,,3 */ + }; + +t_stat mt_boot (int32 unitno, DEVICE *dptr) +{ +uint32 i, chan; +extern t_uint64 *M; + +chan = dptr - &mt_dev[0] + 1; +WriteP (BOOT_START, boot_rom[0] + unitno + (chan << 9)); +for (i = 1; i < 5; i++) + WriteP (BOOT_START + i, boot_rom[i]); +PC = BOOT_START; +return SCPE_OK; +} diff --git a/I7094/i7094_sys.c b/I7094/i7094_sys.c index 1d36e3bc..0c818061 100644 --- a/I7094/i7094_sys.c +++ b/I7094/i7094_sys.c @@ -1,6 +1,6 @@ /* i7094_sys.c: IBM 7094 simulator interface - Copyright (c) 2003-2008, Robert M Supnik + Copyright (c) 2003-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 16-Jul-10 RMS Added SPUx, SPTx, SPRx 29-Oct-06 RMS Added additional expanded core instructions 08-Jun-06 RMS Added Dave Pitts' binary loader */ @@ -154,6 +155,7 @@ return SCPE_NOFNC; #define I_TCH 0130000000000000 /* transfer channel */ #define I_I9N 0140000000000000 /* 7909 with nostore */ #define I_I9S 0150000000000000 /* 7909 */ +#define I_SPX 0160000000000000 /* SPU, SPR */ #define IFAKE_7607 0001000000000000 /* fake op extensions */ #define IFAKE_7909 0002000000000000 #define DFAKE (DMASK|IFAKE_7607|IFAKE_7909) @@ -171,6 +173,7 @@ return SCPE_NOFNC; #define I_N_TCH 013 #define I_N_I9N 014 #define I_N_I9S 015 +#define I_N_SPX 016 #define INST_P_XIT 0 /* exit */ #define INST_P_SKP 1 /* do not print */ @@ -178,16 +181,17 @@ return SCPE_NOFNC; #define INST_P_PNZ 3 /* print if nz */ #define INST_P_PNT 4 /* print if nz, term */ -static const t_uint64 masks[14] = { +static const t_uint64 masks[15] = { 03777700000000, 03777700000000, 03777700000000, 03777700000000, 03777400000000, 03700000000000, 03700000000000, 03777700077777, 03777700000000, 03777700000000, 03700000200000, 03700000200000, - 03760000200000, 03740000200000 }; + 03760000200000, 03740000200000, + 03777700077760 }; -static const uint32 fld_max[14][3] = { /* addr,tag,decr limit */ +static const uint32 fld_max[15][3] = { /* addr,tag,decr limit */ { INST_M_ADDR, INST_M_TAG, 0 }, { INST_M_ADDR, INST_M_TAG, 0 }, { INST_M_ADDR, INST_M_TAG, 0 }, @@ -201,10 +205,11 @@ static const uint32 fld_max[14][3] = { /* addr,tag,decr limit * { INST_M_ADDR, 1, INST_M_DEC }, { INST_M_ADDR, 1, 0 }, { INST_M_ADDR, 1, 0 }, - { INST_M_ADDR, 1, 0 } + { INST_M_ADDR, 1, 0 }, + { INST_M_4B, INST_M_TAG, 0 } }; -static const uint32 fld_fmt[14][3] = { /* addr,tag,decr print */ +static const uint32 fld_fmt[15][3] = { /* addr,tag,decr print */ { INST_P_PNT, INST_P_PNT, INST_P_XIT }, /* nop: all optional */ { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* mxr: tag optional */ { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* mxn: tag optional */ @@ -218,12 +223,13 @@ static const uint32 fld_fmt[14][3] = { /* addr,tag,decr print * { INST_P_PRA, INST_P_PNZ, INST_P_PRA }, /* iox: tag optional */ { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* tch: tag optional */ { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* i9n: tag optional */ - { INST_P_PRA, INST_P_PNT, INST_P_XIT } /* i9s: tag optional */ + { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* i9s: tag optional */ + { INST_P_PNZ, INST_P_PNT, INST_P_XIT } /* SPx: tag optional */ }; -static const t_uint64 ind_test[14] = { - 0, 0, INST_IND, 0, 0, 0, 0, - 0, 0, 0, CHI_IND, CHI_IND, CHI_IND, CHI_IND +static const t_uint64 ind_test[15] = { + 0, 0, INST_IND, 0, 0, 0, 0, 0, + 0, 0, CHI_IND, CHI_IND, CHI_IND, CHI_IND, 0 }; static const char *opcode[] = { @@ -248,6 +254,15 @@ static const char *opcode[] = { "RDCA", "RDCB", "RDCC", "RDCD", "RDCE", "RDCF", "RDCG", "RDCH", + "SPUA", "SPUB", "SPUC", + "SPUD", "SPUE", "SPUF", + "SPUG", "SPUH", + "SPTA", "SPTB", "SPTC", + "SPTD", "SPTE", "SPTF", + "SPTG", "SPTH", + "SPRA", "SPRB", "SPRC", + "SPRD", "SPRE", "SPRF", + "SPRG", "SPRH", "TRCA", "TRCC", "TRCE", "TRCG", @@ -410,6 +425,15 @@ static const t_uint64 opc_v[] = { 0076000001352+I_SNS, 0076000002352+I_SNS, 0076000003352+I_SNS, 0076000004352+I_SNS, 0076000005352+I_SNS, 0076000006352+I_SNS, 0076000007352+I_SNS, 0076000010352+I_SNS, + 0076000001340+I_SNS, 0076000002340+I_SNS, 0076000003340+I_SNS, + 0076000004340+I_SNS, 0076000005340+I_SNS, 0076000006340+I_SNS, + 0076000007340+I_SNS, 0076000010340+I_SNS, + 0076000001360+I_SNS, 0076000002360+I_SNS, 0076000003360+I_SNS, + 0076000004360+I_SNS, 0076000005360+I_SNS, 0076000006360+I_SNS, + 0076000007360+I_SNS, 0076000010360+I_SNS, + 0076000001360+I_SNS, 0076000002360+I_SNS, 0076000003360+I_SNS, + 0076000004360+I_SNS, 0076000005360+I_SNS, 0076000006360+I_SNS, + 0076000007360+I_SNS, 0076000010360+I_SNS, 0002200000000+I_MXN, 0002400000000+I_MXN, 0002600000000+I_MXN, 0002700000000+I_MXN, @@ -618,7 +642,8 @@ for (i = 0; opc_v[i] > 0; i++) { /* loop thru ops */ switch (fmt) { /* case on format */ case INST_P_PNT: /* print nz, else term */ - for (l = k, c = 0; l < 3; l++) c |= fld[k]; + for (l = k, c = 0; l < 3; l++) + c |= fld[k]; if (c == 0) return SCPE_OK; case INST_P_PNZ: /* print non-zero */ diff --git a/I7094/i7094_sys_old.c b/I7094/i7094_sys_old.c new file mode 100644 index 00000000..1d36e3bc --- /dev/null +++ b/I7094/i7094_sys_old.c @@ -0,0 +1,748 @@ +/* i7094_sys.c: IBM 7094 simulator interface + + Copyright (c) 2003-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 29-Oct-06 RMS Added additional expanded core instructions + 08-Jun-06 RMS Added Dave Pitts' binary loader +*/ + +#include "i7094_defs.h" +#include +#include "i7094_dat.h" + +extern DEVICE cpu_dev; +extern DEVICE ch_dev[NUM_CHAN]; +extern DEVICE mt_dev[NUM_CHAN]; +extern DEVICE drm_dev; +extern DEVICE dsk_dev; +extern DEVICE com_dev, coml_dev; +extern DEVICE cdr_dev, cdp_dev; +extern DEVICE lpt_dev; +extern DEVICE clk_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; + +uint32 cvt_code_to_ascii (uint32 c, int32 sw); +uint32 cvt_ascii_to_code (uint32 c, int32 sw); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "IBM 7094"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + &clk_dev, + &ch_dev[0], + &ch_dev[1], + &ch_dev[2], + &ch_dev[3], + &ch_dev[4], + &ch_dev[5], + &ch_dev[6], + &ch_dev[7], + &mt_dev[0], + &mt_dev[1], + &mt_dev[2], + &mt_dev[3], + &mt_dev[4], + &mt_dev[5], + &mt_dev[6], + &mt_dev[7], + &cdr_dev, + &cdp_dev, + &lpt_dev, + &dsk_dev, + &drm_dev, + &com_dev, + &coml_dev, + NULL + }; + +char ch_bkpt_msg[] = "Channel A breakpoint, CLC: xxxxxx"; + +const char *sim_stop_messages[] = { + "Unknown error", + "HALT instruction", + "Breakpoint", + "Undefined instruction", + "Divide check", + "Nested XEC limit exceeded", + "Address stop", + "Non-existent channel", + "Illegal instruction for 7909 channel", + "Illegal instruction for non-7909 channel", + "Non-existent device", + "Undefined channel instruction", + "Write to protected device", + "Illegal instruction for device", + "Invalid 7631 track format", + "7750 buffer pool empty on input", + "7750 buffer pool empty on output", + "7750 invalid line number", + "7750 invalid message", + ch_bkpt_msg + }; + +/* Modify channel breakpoint message */ + +t_stat ch_bkpt (uint32 ch, uint32 clc) +{ +ch_bkpt_msg[8] = 'A' + ch; +sprintf (&ch_bkpt_msg[27], "%06o", clc); +return STOP_CHBKPT; +} + +/* Binary loader, not implemented */ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +extern t_stat binloader (FILE *fd, char *file, int loadpt); + +if (flag == 0) + return binloader (fileref, cptr, 0); +return SCPE_NOFNC; +} + +/* Symbol tables */ + +#define I_V_FL 39 /* inst class */ +#define I_M_FL 017 /* class mask */ +#define I_NOP 0000000000000000 /* no operand */ +#define I_MXR 0010000000000000 /* addr(tag) */ +#define I_MXN 0020000000000000 /* *addr(tag) */ +#define I_MXV 0030000000000000 /* var mul/div */ +#define I_MXC 0040000000000000 /* convert */ +#define I_DNP 0050000000000000 /* decr, no oper */ +#define I_DEC 0060000000000000 /* decrement */ +#define I_SNS 0070000000000000 /* sense */ +#define I_IMM 0100000000000000 /* 18b immediate */ +#define I_TAG 0110000000000000 /* tag only */ +#define I_IOX 0120000000000000 /* IO channel */ +#define I_TCH 0130000000000000 /* transfer channel */ +#define I_I9N 0140000000000000 /* 7909 with nostore */ +#define I_I9S 0150000000000000 /* 7909 */ +#define IFAKE_7607 0001000000000000 /* fake op extensions */ +#define IFAKE_7909 0002000000000000 +#define DFAKE (DMASK|IFAKE_7607|IFAKE_7909) +#define I_N_NOP 000 +#define I_N_MXR 001 +#define I_N_MXN 002 +#define I_N_MXV 003 +#define I_N_MXC 004 +#define I_N_DNP 005 +#define I_N_DEC 006 +#define I_N_SNS 007 +#define I_N_IMM 010 +#define I_N_TAG 011 +#define I_N_IOX 012 +#define I_N_TCH 013 +#define I_N_I9N 014 +#define I_N_I9S 015 + +#define INST_P_XIT 0 /* exit */ +#define INST_P_SKP 1 /* do not print */ +#define INST_P_PRA 2 /* print always */ +#define INST_P_PNZ 3 /* print if nz */ +#define INST_P_PNT 4 /* print if nz, term */ + +static const t_uint64 masks[14] = { + 03777700000000, 03777700000000, + 03777700000000, 03777700000000, + 03777400000000, 03700000000000, + 03700000000000, 03777700077777, + 03777700000000, 03777700000000, + 03700000200000, 03700000200000, + 03760000200000, 03740000200000 }; + +static const uint32 fld_max[14][3] = { /* addr,tag,decr limit */ + { INST_M_ADDR, INST_M_TAG, 0 }, + { INST_M_ADDR, INST_M_TAG, 0 }, + { INST_M_ADDR, INST_M_TAG, 0 }, + { INST_M_ADDR, INST_M_TAG, INST_M_VCNT }, + { INST_M_ADDR, INST_M_TAG, INST_M_CCNT }, + { INST_M_ADDR, INST_M_TAG, INST_M_DEC }, + { INST_M_ADDR, INST_M_TAG, INST_M_DEC }, + { 0, INST_M_TAG, 0 }, + { RMASK, 0, 0 }, + { INST_M_ADDR, INST_M_TAG, 0 }, + { INST_M_ADDR, 1, INST_M_DEC }, + { INST_M_ADDR, 1, 0 }, + { INST_M_ADDR, 1, 0 }, + { INST_M_ADDR, 1, 0 } + }; + +static const uint32 fld_fmt[14][3] = { /* addr,tag,decr print */ + { INST_P_PNT, INST_P_PNT, INST_P_XIT }, /* nop: all optional */ + { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* mxr: tag optional */ + { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* mxn: tag optional */ + { INST_P_PRA, INST_P_PNZ, INST_P_PRA }, /* mxv: tag optional */ + { INST_P_PRA, INST_P_PNZ, INST_P_PRA }, /* cvt: tag optional */ + { INST_P_PNT, INST_P_PNT, INST_P_PNT }, /* dnp: all optional */ + { INST_P_PRA, INST_P_PRA, INST_P_PRA }, /* dec: print all */ + { INST_P_SKP, INST_P_PNT, INST_P_XIT }, /* sns: skip addr, tag opt */ + { INST_P_PRA, INST_P_XIT, INST_P_XIT }, /* immediate: addr only */ + { INST_P_PNZ, INST_P_PRA, INST_P_XIT }, /* tag: addr optional */ + { INST_P_PRA, INST_P_PNZ, INST_P_PRA }, /* iox: tag optional */ + { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* tch: tag optional */ + { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* i9n: tag optional */ + { INST_P_PRA, INST_P_PNT, INST_P_XIT } /* i9s: tag optional */ + }; + +static const t_uint64 ind_test[14] = { + 0, 0, INST_IND, 0, 0, 0, 0, + 0, 0, 0, CHI_IND, CHI_IND, CHI_IND, CHI_IND + }; + +static const char *opcode[] = { + "TXI", "TIX", "TXH", + "STR", "TNX", "TXL", + "HTR", "TRA", "TTR", + + "CLM", "LBT", "CHS", + "SSP", "ENK", "IOT", + "COM", "ETM", "RND", + "FRN", "DCT", "RCT", + "LMTM", "SLF", "SLN1", + "SLN2", "SLN3", "SLN4", + "SWT1", "SWT2", "SWT3", + "SWT4", "SWT5", "SWT6", + "BTTA", "BTTB", "BTTC", + "BTTD", "BTTE", "BTTF", + "BTTG", "BTTH", + "RICA", "RICB", "RICC", + "RICD", "RICE", "RICF", + "RICG", "RICH", + "RDCA", "RDCB", "RDCC", + "RDCD", "RDCE", "RDCF", + "RDCG", "RDCH", + + "TRCA", "TRCC", + "TRCE", "TRCG", + "TEFA", "TEFC", + "TEFE", "TEFG", + "TLQ", "IIA", "TIO", + "OAI", "PAI", "TIF", + "IIR", "RFT", "SIR", + "RNT", "RIR", + "TCOA", "TCOB", "TCOC", + "TCOD", "TCOE", "TCOF", + "TCOG", "TCOH", "TSX", + "TZE", "CVR", "TPL", + "XCA", "TOV", + "TQO", "TQP", + "MPY", "VLM", "VLM1", + "DVH", "DVP", + "VDH", "VDP", + "VDH2", "VDP2", + "FDH", "FDP", + "FMP", "DFMP", + "FAD", "DFAD", + "FSB", "DFSB", + "FAM", "DFAM", + "FSM", "DFSM", + "ANS", "ERA", + "CAS", "ACL", + "ADD", "ADM", + "SUB", "SBM", + "HPR", "IIS", "LDI", + "OSI", "DLD", "OFT", + "RIS", "ONT", + "CLA", "CLS", + "ZET", "XEC", + "LXA", "LAC", + "RCHA", "RCHC", + "RCHE", "RCHG", + "LCHA", "LCHC", + "LCHE", "LCHG", + "RSCA", "RSCC", + "RSCE", "RSCG", + "STCA", "STCC", + "STCE", "STCG", + "LDQ", "ENB", + "STZ", "STO", "SLW", + "STI", "STA", "STD", + "STT", "STP", + "SXA", "SCA", + "SCHA", "SCHC", + "SCHE", "SCHG", + "SCDA", "SCDC", + "SCDE", "SCDG", + "PAX", "PAC", + "PXA", "PCA", + "PSE", "NOP", "RDS", + "LLS", "BSR", "LRS", + "WRS", "ALS", "WEF", + "ARS", "REW", "AXT", + "SDN", + + "CLM", "PBT", "EFTM", + "SSM", "LFTM", "ESTM", + "ECTM", "LTM", "LSNM", + "EMTM", "SLT1", "SLT2", + "SLT3", "SLT4", + "ETTA", "ETTB", "ETTC", + "ETTD", "ETTE", "ETTF", + "ETTG", "ETTH", + + "ESNT", + "TRCB", "TRCD", + "TRCF", "TRCH", + "TEFB", "TEFD", + "TEFF", "TEFH", + "RIA", "PIA", + "IIL", "LFT", "SIL", + "LNT", "RIL", + "TCNA", "TCNB", "TCNC", + "TCND", "TCNE", "TCNF", + "TCNG", "TCNH", + "TNZ", "CVR", "TMI", + "XCL", "TNO", "CRQ", + "MPR", "DFDH", "DFDP", + "UFM", "DUFM", + "UFA", "DUFA", + "UFS", "DUFS", + "UAM", "DUAM", + "USM", "DUSM", + "ANA", "LAS", + "CAL", "ORA", "NZT", + "LXD", "LXC", + "RCHB", "RCHD", + "RCHF", "RCHH", + "LCHB", "LCHD", + "LCHF", "LCHH", + "RSCB", "RSCD", + "RSCF", "RSCH", + "STCB", "STCD", + "STCF", "STCH", + "STQ", "ORS", "DST", + "SLQ", "STL", + "SXD", "SCD", + "SCHB", "SCHD", + "SCHF", "SCHH", + "SCDB", "SCDD", + "SCDF", "SCDH", + "PDX", "PDC", + "PXD", "PCD", + "MSE", "LGL", "BSF", + "LGR", "RQL", "RUN", + "AXC", + + "TIA", "TIB", + "LRI", "LPI", + "SEA", "SEB", + "IFT", "EFT", + + "IOCD", "IOCDN", "TCH", + "IORP", "IORPN", + "IORT", "IORTN", + "IOCP", "IOCPN", + "IOCT", "IOCTN", + "IOSP", "IOSPN", + "IOST", "IOSTN", + + "WTR", "XMT", + "TCH", "LIPT", + "CTL", "CTLN", + "CTLR", "CTLRN", + "CTLW", "CTLWN", + "SNS", + "LAR", "SAR", "TWT", + "CPYP", + "CPYD", "TCM", + "LIP", "TDC", "LCC", + "SMS", "ICC", + + NULL + }; + +static const t_uint64 opc_v[] = { + 0100000000000+I_DEC, 0200000000000+I_DEC, 0300000000000+I_DEC, + 0500000000000+I_DNP, 0600000000000+I_DEC, 0700000000000+I_DEC, + 0000000000000+I_MXN, 0002000000000+I_MXN, 0002100000000+I_MXN, + + 0076000000000+I_SNS, 0076000000001+I_SNS, 0076000000002+I_SNS, + 0076000000003+I_SNS, 0076000000004+I_SNS, 0076000000005+I_SNS, + 0076000000006+I_SNS, 0076000000007+I_SNS, 0076000000010+I_SNS, + 0076000000011+I_SNS, 0076000000012+I_SNS, 0076000000014+I_SNS, + 0076000000016+I_SNS, 0076000000140+I_SNS, 0076000000141+I_SNS, + 0076000000142+I_SNS, 0076000000143+I_SNS, 0076000000144+I_SNS, + 0076000000161+I_SNS, 0076000000162+I_SNS, 0076000000163+I_SNS, + 0076000000164+I_SNS, 0076000000165+I_SNS, 0076000000166+I_SNS, + 0076000001000+I_SNS, 0076000002000+I_SNS, 0076000003000+I_SNS, + 0076000004000+I_SNS, 0076000005000+I_SNS, 0076000006000+I_SNS, + 0076000007000+I_SNS, 0076000010000+I_SNS, + 0076000001350+I_SNS, 0076000002350+I_SNS, 0076000003350+I_SNS, + 0076000004350+I_SNS, 0076000005350+I_SNS, 0076000006350+I_SNS, + 0076000007350+I_SNS, 0076000010350+I_SNS, + 0076000001352+I_SNS, 0076000002352+I_SNS, 0076000003352+I_SNS, + 0076000004352+I_SNS, 0076000005352+I_SNS, 0076000006352+I_SNS, + 0076000007352+I_SNS, 0076000010352+I_SNS, + + 0002200000000+I_MXN, 0002400000000+I_MXN, + 0002600000000+I_MXN, 0002700000000+I_MXN, + 0003000000000+I_MXN, 0003100000000+I_MXN, + 0003200000000+I_MXN, 0003300000000+I_MXN, + 0004000000000+I_MXN, 0004100000000+I_NOP, 0004200000000+I_MXR, + 0004300000000+I_NOP, 0004400000000+I_NOP, 0004600000000+I_MXR, + 0005100000000+I_IMM, 0005400000000+I_IMM, 0005500000000+I_IMM, + 0005600000000+I_IMM, 0005700000000+I_IMM, + 0006000000000+I_MXN, 0006100000000+I_MXN, 0006200000000+I_MXN, + 0006300000000+I_MXN, 0006400000000+I_MXN, 0006500000000+I_MXN, + 0006600000000+I_MXN, 0006700000000+I_MXN, 0007400000000+I_MXR, + 0010000000000+I_MXN, 0011400000000+I_MXC, 0012000000000+I_MXN, + 0013100000000+I_NOP, 0014000000000+I_MXN, + 0016100000000+I_MXN, 0016200000000+I_MXN, + 0020000000000+I_MXN, 0020400000000+I_MXV, 0020500000000+I_MXV, + 0022000000000+I_MXN, 0022100000000+I_MXN, + 0022400000000+I_MXV, 0022500000000+I_MXV, + 0022600000000+I_MXV, 0022700000000+I_MXV, + 0024000000000+I_MXN, 0024100000000+I_MXN, + 0026000000000+I_MXN, 0026100000000+I_MXN, + 0030000000000+I_MXN, 0030100000000+I_MXN, + 0030200000000+I_MXN, 0030300000000+I_MXN, + 0030400000000+I_MXN, 0030500000000+I_MXN, + 0030600000000+I_MXN, 0030700000000+I_MXN, + 0032000000000+I_MXN, 0032200000000+I_MXN, + 0034000000000+I_MXN, 0036100000000+I_MXN, + 0040000000000+I_MXN, 0040100000000+I_MXN, + 0040200000000+I_MXN, 0440000000000+I_MXN, + 0042000000000+I_NOP, 0044000000000+I_MXN, 0044100000000+I_MXN, + 0044200000000+I_MXN, 0044300000000+I_MXN, 0044400000000+I_MXN, + 0044500000000+I_MXN, 0044600000000+I_MXN, + 0050000000000+I_MXN, 0050200000000+I_MXN, + 0052000000000+I_MXN, 0052200000000+I_MXN, + 0053400000000+I_MXR, 0053500000000+I_MXR, + 0054000000000+I_MXN, 0054100000000+I_MXN, + 0054200000000+I_MXN, 0054300000000+I_MXN, + 0054400000000+I_MXN, 0054500000000+I_MXN, + 0054600000000+I_MXN, 0054700000000+I_MXN, + 0054000000000+I_MXN, 0054100000000+I_MXN, + 0054200000000+I_MXN, 0054300000000+I_MXN, + 0054400000000+I_MXN, 0054500000000+I_MXN, + 0054600000000+I_MXN, 0054700000000+I_MXN, + 0056000000000+I_MXN, 0056400000000+I_MXN, + 0060000000000+I_MXN, 0060100000000+I_MXN, 0060200000000+I_MXN, + 0060400000000+I_MXN, 0062100000000+I_MXN, 0062200000000+I_MXN, + 0062500000000+I_MXN, 0063000000000+I_MXN, + 0063400000000+I_MXR, 0063600000000+I_MXR, + 0064000000000+I_MXN, 0064000000000+I_MXN, + 0064200000000+I_MXN, 0064300000000+I_MXN, + 0064400000000+I_MXN, 0064500000000+I_MXN, + 0064600000000+I_MXN, 0064700000000+I_MXN, + 0073400000000+I_TAG, 0073700000000+I_TAG, + 0075400000000+I_TAG, 0075600000000+I_TAG, + 0076000000000+I_MXR, 0076100000000+I_NOP, 0076200000000+I_MXR, + 0076300000000+I_MXR, 0076400000000+I_MXR, 0076500000000+I_MXR, + 0076600000000+I_MXR, 0076700000000+I_MXR, 0077000000000+I_MXR, + 0077100000000+I_MXR, 0077200000000+I_MXR, 0077400000000+I_MXR, + 0077600000000+I_MXR, + + 0476000000000+I_SNS, 0476000000001+I_SNS, 0476000000002+I_SNS, + 0476000000003+I_SNS, 0476000000004+I_SNS, 0476000000005+I_SNS, + 0476000000006+I_SNS, 0476000000007+I_SNS, 0476000000010+I_SNS, + 0476000000016+I_SNS, 0476000000141+I_SNS, 0476000000142+I_SNS, + 0476000000143+I_SNS, 0476000000144+I_SNS, + 0476000001000+I_SNS, 0476000002000+I_SNS, 0476000003000+I_SNS, + 0476000004000+I_SNS, 0476000005000+I_SNS, 0476000006000+I_SNS, + 0476000007000+I_SNS, 0476000010000+I_SNS, + + 0402100000000+I_MXN, + 0402200000000+I_MXN, 0402400000000+I_MXN, + 0402600000000+I_MXN, 0402700000000+I_MXN, + 0403000000000+I_MXN, 0403100000000+I_MXN, + 0403200000000+I_MXN, 0403300000000+I_MXN, + 0404200000000+I_NOP, 0404600000000+I_NOP, + 0405100000000+I_IMM, 0405400000000+I_IMM, 0405500000000+I_IMM, + 0405600000000+I_IMM, 0405700000000+I_IMM, + 0406000000000+I_MXN, 0406100000000+I_MXN, 0406200000000+I_MXN, + 0406300000000+I_MXN, 0406400000000+I_MXN, 0406500000000+I_MXN, + 0406600000000+I_MXN, 0406700000000+I_MXN, + 0410000000000+I_MXN, 0411400000000+I_MXC, 0412000000000+I_MXN, + 0413000000000+I_NOP, 0414000000000+I_MXN, 0415400000000+I_MXC, + 0420000000000+I_MXN, 0424000000000+I_MXN, 0424100000000+I_MXN, + 0426000000000+I_MXN, 0426100000000+I_MXN, + 0430000000000+I_MXN, 0430100000000+I_MXN, + 0430200000000+I_MXN, 0430300000000+I_MXN, + 0430400000000+I_MXN, 0430500000000+I_MXN, + 0430600000000+I_MXN, 0430700000000+I_MXN, + 0432000000000+I_MXN, 0434000000000+I_MXN, + 0450000000000+I_MXN, 0450100000000+I_MXN, 0452000000000+I_MXN, + 0453400000000+I_MXR, 0453500000000+I_MXR, + 0454000000000+I_MXN, 0454100000000+I_MXN, + 0454200000000+I_MXN, 0454300000000+I_MXN, + 0454400000000+I_MXN, 0454500000000+I_MXN, + 0454600000000+I_MXN, 0454700000000+I_MXN, + 0454000000000+I_MXN, 0454100000000+I_MXN, + 0454200000000+I_MXN, 0454300000000+I_MXN, + 0454400000000+I_MXN, 0454500000000+I_MXN, + 0454600000000+I_MXN, 0454700000000+I_MXN, + 0460000000000+I_MXN, 0460200000000+I_MXN, 0460300000000+I_MXN, + 0462000000000+I_MXN, 0462500000000+I_MXN, + 0463400000000+I_MXR, 0463600000000+I_MXR, + 0464000000000+I_MXN, 0464000000000+I_MXN, + 0464200000000+I_MXN, 0464300000000+I_MXN, + 0464400000000+I_MXN, 0464500000000+I_MXN, + 0464600000000+I_MXN, 0464700000000+I_MXN, + 0473400000000+I_TAG, 0473700000000+I_TAG, + 0475400000000+I_TAG, 0475600000000+I_TAG, + 0476000000000+I_MXR, 0476300000000+I_MXR, 0476400000000+I_MXR, + 0476500000000+I_MXR, 0477300000000+I_MXR, 0477200000000+I_MXR, + 0477400000000+I_MXR, + + 0010100000000+I_MXN, 0410100000000+I_MXN, + 0056200000000+I_MXN, 0456400000000+I_MXN, + 0476100000041+I_SNS, 0476100000042+I_SNS, + 0476100000043+I_SNS, 0476100000044+I_SNS, + + 01000000000000+I_IOX, 01000000200000+I_IOX, 01100000000000+I_TCH, + 01200000000000+I_IOX, 01200000200000+I_IOX, + 01300000000000+I_IOX, 01300000200000+I_IOX, + 01400000000000+I_IOX, 01400000200000+I_IOX, + 01500000000000+I_IOX, 01500000200000+I_IOX, + 01600000000000+I_IOX, 01600000200000+I_IOX, + 01700000000000+I_IOX, 01700000200000+I_IOX, + + 02000000000000+I_TCH, 02000000200000+I_IOX, + 02100000000000+I_TCH, 02100000200000+I_TCH, + 02200000000000+I_I9N, 02220000000000+I_TCH, + 02200000200000+I_I9N, 02220000200000+I_TCH, + 02240000000000+I_I9N, 02260000000000+I_TCH, + 02240000200000+I_I9N, + 02300000000000+I_I9S, 02300000200000+I_I9S, + 02340000000000+I_I9S, + 02400000000000+I_IOX, + 02500000000000+I_IOX, 02500000200000+I_IOX, + 02600000200000+I_I9S, 02640000000000+I_I9S, 02640000200000+I_I9S, + 02700000000000+I_I9S, 02700000200000+I_IOX, + + 0 + }; + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +uint32 i, j, k, l, fmt, c, fld[3]; +DEVICE *dptr; +t_uint64 inst; + +inst = val[0]; +if (uptr == NULL) + uptr = &cpu_unit; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) + return SCPE_IERR; + +if (sw & SWMASK ('C')) { /* character? */ + c = (uint32) (inst & 077); + fprintf (of, "%c", cvt_code_to_ascii (c, sw)); + return SCPE_OK; + } +if (sw & SWMASK ('S')) { /* string? */ + for (i = 36; i > 0; i = i - 6) { + c = (uint32) ((inst >> (i - 6)) & 077); + fprintf (of, "%c", cvt_code_to_ascii (c, sw)); + } + return SCPE_OK; + } +if (!(sw & (SWMASK ('M')|SWMASK ('I')|SWMASK ('N'))) || /* M, N or I? */ + (dptr->dwidth != 36)) + return SCPE_ARG; + +/* Instruction decode */ + +fld[0] = ((uint32) inst & 0777777); +fld[1] = GET_TAG (inst); /* get 3 fields */ +fld[2] = GET_DEC (inst); +if (sw & SWMASK ('I')) /* decode as 7607? */ + inst |= IFAKE_7607; +if (sw & SWMASK ('N')) /* decode as 7909? */ + inst |= IFAKE_7909; + +for (i = 0; opc_v[i] > 0; i++) { /* loop thru ops */ + j = (int32) ((opc_v[i] >> I_V_FL) & I_M_FL); /* get class */ + if ((opc_v[i] & DFAKE) == (inst & masks[j])) { /* match? */ + if (inst & ind_test[j]) /* indirect? */ + fprintf (of, "%s*", opcode[i]); + else fprintf (of, "%s", opcode[i]); /* opcode */ + for (k = 0; k < 3; k++) + fld[k] = fld[k] & fld_max[j][k]; + for (k = 0; k < 3; k++) { /* loop thru fields */ + fmt = fld_fmt[j][k]; /* get format */ + if (fmt == INST_P_XIT) + return SCPE_OK; + switch (fmt) { /* case on format */ + + case INST_P_PNT: /* print nz, else term */ + for (l = k, c = 0; l < 3; l++) c |= fld[k]; + if (c == 0) + return SCPE_OK; + case INST_P_PNZ: /* print non-zero */ + fputc (k? ',': ' ', of); + if (fld[k]) + fprintf (of, "%-o", fld[k]); + break; + case INST_P_PRA: /* print always */ + fputc (k? ',': ' ', of); + fprintf (of, "%-o", fld[k]); + break; + case INST_P_SKP: /* skip */ + break; + } /* end switch */ + } /* end for k */ + return SCPE_OK; /* done */ + } /* end if */ + } /* end for i */ +return SCPE_ARG; +} + +/* Convert character to code to ASCII + + -b BCD + -a business-chain */ + +uint32 cvt_code_to_ascii (uint32 c, int32 sw) +{ +if (sw & SWMASK ('B')) { + if (sw & SWMASK ('A')) + return bcd_to_ascii_a[c]; + else return bcd_to_ascii_h[c]; + } +else if (sw & SWMASK ('A')) + return nine_to_ascii_a[c]; +else return nine_to_ascii_h[c]; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +uint32 i, j, c; +t_uint64 fld[3]; +t_bool ind; +t_stat r; +char gbuf[CBUFSIZE]; + +while (isspace (*cptr)) cptr++; +if ((sw & SWMASK ('C')) || ((*cptr == '\'') && cptr++)) { /* character? */ + if (cptr[0] == 0) /* must have 1 char */ + return SCPE_ARG; + val[0] = (t_value) cvt_ascii_to_code (cptr[0] & 0177, sw); + return SCPE_OK; + } +if ((sw & SWMASK ('S')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ + if (cptr[0] == 0) /* must have 1 char */ + return SCPE_ARG; + for (i = 0; i < 6; i++) { + c = cptr[0] & 0177; + if (c) + val[0] = (val[0] << 6) | ((t_value) cvt_ascii_to_code (c, sw)); + else { + val[0] = val[0] << (6 * (6 - i)); + break; + } + } + return SCPE_OK; + } + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +j = strlen (gbuf); /* get length */ +if (gbuf[j - 1] == '*') { /* indirect? */ + ind = TRUE; + gbuf[j - 1] = 0; + } +else ind = FALSE; +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) + return SCPE_ARG; +j = (uint32) ((opc_v[i] >> I_V_FL) & I_M_FL); /* get class */ +val[0] = opc_v[i] & DMASK; +if (ind) { + if (ind_test[j]) + val[0] |= ind_test[j]; + else return SCPE_ARG; + } + +for (i = 0; i < 3; i++) /* clear inputs */ + fld[i] = 0; +for (i = 0; (i < 3) && *cptr; i++) { /* parse inputs */ + if (i < 2) /* get glyph */ + cptr = get_glyph (cptr, gbuf, ','); + else cptr = get_glyph (cptr, gbuf, 0); + if (gbuf[0]) { /* anything? */ + fld[i] = get_uint (gbuf, 8, fld_max[j][i], &r); + if ((r != SCPE_OK) || (fld_max[j][i] == 0)) + return SCPE_ARG; + } + } +if (*cptr != 0) /* junk at end? */ + return SCPE_ARG; + +val[0] = val[0] | fld[0] | (fld[1] << INST_V_TAG) | (fld[2] << INST_V_DEC); +return SCPE_OK; +} + +/* Convert ASCII to character code + + -b BCD */ + +uint32 cvt_ascii_to_code (uint32 c, int32 sw) +{ +if (sw & SWMASK ('B')) + return ascii_to_bcd[c]; +else return ascii_to_nine[c]; +} diff --git a/Interdata/id_defs.h b/Interdata/id_defs.h index 799b76ea..77b643dd 100644 --- a/Interdata/id_defs.h +++ b/Interdata/id_defs.h @@ -1,6 +1,6 @@ /* id_defs.h: Interdata 16b/32b simulator definitions - Copyright (c) 2000-2006, Robert M. Supnik + Copyright (c) 2000-2010, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,6 +26,7 @@ The author gratefully acknowledges the help of Carl Friend and Al Kossow, who provided key documents about the Interdata product line. + 22-May-10 RMS Added check for 64b definitions 09-Mar-06 RMS Increased register sets to architectural limit 25-Jan-04 RMS Removed local logging support 22-Sep-03 RMS Added additional instruction decode types @@ -39,6 +40,10 @@ #include "sim_defs.h" /* simulator defns */ +#if defined(USE_INT64) || defined(USE_ADDR64) +#error "Interdata 16/32 does not support 64b values!" +#endif + /* Simulator stop codes */ #define STOP_RSRV 1 /* undef instr */ diff --git a/LGP/lgp_defs.h b/LGP/lgp_defs.h index fe54057d..aed3fe04 100644 --- a/LGP/lgp_defs.h +++ b/LGP/lgp_defs.h @@ -1,6 +1,6 @@ /* lgp_defs.h: LGP simulator definitions - Copyright (c) 2004-2008, Robert M. Supnik + Copyright (c) 2004-2010, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -22,6 +22,9 @@ Except as contained in this notice, the name of Robert M Supnik shall not be be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + + 22-May-10 RMS Added check for 64b definitions + */ #ifndef _LGP_DEFS_H_ @@ -29,6 +32,10 @@ #include "sim_defs.h" /* simulator defns */ +#if defined(USE_INT64) || defined(USE_ADDR64) +#error "LGP-30 does not support 64b values!" +#endif + /* Simulator stop codes */ #define STOP_STOP 1 /* STOP */ diff --git a/NOVA/nova_defs.h b/NOVA/nova_defs.h index b5edbd36..b21d573a 100644 --- a/NOVA/nova_defs.h +++ b/NOVA/nova_defs.h @@ -1,6 +1,6 @@ /* nova_defs.h: NOVA/Eclipse simulator definitions - Copyright (c) 1993-2008, Robert M. Supnik + Copyright (c) 1993-2010, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 22-May-10 RMS Added check for 64b definitions 04-Jul-07 BKR BUSY/DONE/INTR "convenience" macros added, INT_TRAP added for Nova 3, 4 trap instruction handling, support for 3rd-party 64KW Nova extensions added, @@ -49,6 +50,10 @@ #include "sim_defs.h" /* simulator defns */ +#if defined(USE_INT64) || defined(USE_ADDR64) +#error "Nova does not support 64b values!" +#endif + /* Simulator stop codes */ #define STOP_RSRV 1 /* must be 1 */ diff --git a/PDP1/pdp1_defs.h b/PDP1/pdp1_defs.h index 47290d26..ed051342 100644 --- a/PDP1/pdp1_defs.h +++ b/PDP1/pdp1_defs.h @@ -1,6 +1,6 @@ /* pdp1_defs.h: 18b PDP simulator definitions - Copyright (c) 1993-2008, Robert M. Supnik + Copyright (c) 1993-2010, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 22-May-10 RMS Added check for 64b definitions 21-Dec-06 RMS Added 16-channel sequence break support 22-Jul-05 RMS Fixed definition of CPLS_DPY 08-Feb-04 PLB Added support for display @@ -53,6 +54,10 @@ #include "sim_defs.h" +#if defined(USE_INT64) || defined(USE_ADDR64) +#error "PDP-1 does not support 64b values!" +#endif + /* Simulator stop codes */ #define STOP_RSRV 1 /* must be 1 */ diff --git a/PDP10/pdp10_defs.h b/PDP10/pdp10_defs.h index 5c8bfc30..a6eea9cb 100644 --- a/PDP10/pdp10_defs.h +++ b/PDP10/pdp10_defs.h @@ -1,6 +1,6 @@ /* pdp10_defs.h: PDP-10 simulator definitions - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 22-May-10 RMS Added check for 64b addresses 01-Feb-07 RMS Added CD support 29-Oct-06 RMS Added clock coscheduling function 29-Dec-03 RMS Added Q18 definition for PDP11 compatibility @@ -49,6 +50,10 @@ #include "sim_defs.h" /* simulator defns */ +#if defined(USE_ADDR64) +#error "PDP-10 does not support 64b addresses!" +#endif + /* Digital Equipment Corporation's 36b family had six implementations: name mips comments diff --git a/PDP11/pdp11_defs.h b/PDP11/pdp11_defs.h index 4255a53b..269c1cd3 100644 --- a/PDP11/pdp11_defs.h +++ b/PDP11/pdp11_defs.h @@ -1,6 +1,6 @@ /* pdp11_defs.h: PDP-11 simulator definitions - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,6 +26,7 @@ The author gratefully acknowledges the help of Max Burnet, Megan Gentry, and John Wilson in resolving questions about the PDP-11 + 22-May-10 RMS Added check for 64b definitions 19-Nov-08 RMS Moved I/O support routines to I/O library 16-May-08 RMS Added KE11A, DC11 support 02-Feb-08 RMS Fixed DMA memory address limit test (found by John Dundas) @@ -80,6 +81,10 @@ #include "sim_defs.h" /* simulator defns */ #include +#if defined(USE_INT64) || defined(USE_ADDR64) +#error "PDP-11 does not support 64b values!" +#endif + /* Architectural constants */ #define STKL_R 0340 /* stack limit */ diff --git a/PDP11/pdp11_tq.c b/PDP11/pdp11_tq.c index 7479f513..27be77e5 100644 --- a/PDP11/pdp11_tq.c +++ b/PDP11/pdp11_tq.c @@ -1,6 +1,6 @@ /* pdp11_tq.c: TMSCP tape controller simulator - Copyright (c) 2002-2008, Robert M Supnik + Copyright (c) 2002-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,20 @@ tq TQK50 tape controller + 14-Jan-11 MP Various fixes discovered while exploring Ultrix issue: + - Set UNIT_SXC flag when a tape mark is encountered + during forward motion read operations + - Fixed logic which clears UNIT_SXC to check command + modifier + - Added CMF_WR flag to tq_cmf entry for OP_WTM + - Made non-immediate rewind positioning operations + take 2 seconds + - Added UNIT_IDLE flag to tq units + - Fixed debug output of tape file positions when they + are 64b + - Added more debug output after positioning operations. + - Added textual display of the command being performed + 23-Dec-10 RMS Fixed comments about register addresses 18-Jun-07 RMS Added UNIT_IDLE flag to timer thread 16-Feb-06 RMS Revised for new magtape capacity checking 31-Oct-05 RMS Fixed address width for large files @@ -223,7 +237,6 @@ static struct drvtyp drv_tab[] = { extern int32 int_req[IPL_HLVL]; extern int32 tmr_poll, clk_tps; -extern UNIT cpu_unit; extern FILE *sim_deb; extern uint32 sim_taddr_64; @@ -249,6 +262,7 @@ int32 tq_itime = 200; /* init time, except */ int32 tq_itime4 = 10; /* stage 4 */ int32 tq_qtime = 200; /* queue time */ int32 tq_xtime = 500; /* transfer time */ +int32 tq_rwtime = 2000000; /* rewind time 2 sec (adjusted later) */ int32 tq_typ = INIT_TYPE; /* device type */ /* Command table - legal modifiers (low 16b) and flags (high 16b) */ @@ -280,7 +294,7 @@ static uint32 tq_cmf[64] = { CMF_SEQ|CMF_RW|CMF_WR|MD_CDL|MD_CSE|MD_IMM| /* write */ MD_CMP|MD_ERW|MD_SEC|MD_SER, 0, /* 35 */ - CMF_SEQ|MD_CDL|MD_CSE|MD_IMM, /* wr tape mark */ + CMF_SEQ|CMF_WR|MD_CDL|MD_CSE|MD_IMM, /* wr tape mark */ CMF_SEQ|MD_CDL|MD_CSE|MD_IMM|MD_OBC| /* reposition */ MD_REV|MD_RWD|MD_DLE| MD_SCH|MD_SEC|MD_SER, @@ -289,6 +303,37 @@ static uint32 tq_cmf[64] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +static char *tq_cmdname[] = { + "", /* 0 */ + "ABO", /* 1 b: abort */ + "GCS", /* 2 b: get command status */ + "GUS", /* 3 b: get unit status */ + "SCC", /* 4 b: set controller char */ + "","","", /* 5-7 */ + "AVL", /* 8 b: available */ + "ONL", /* 9 b: online */ + "SUC", /* 10 b: set unit char */ + "DAP", /* 11 b: det acc paths - nop */ + "","","","", /* 12-15 */ + "ACC", /* 16 b: access */ + "CCD", /* 17 d: compare - nop */ + "ERS", /* 18 b: erase */ + "FLU", /* 19 d: flush - nop */ + "","", /* 20-21 */ + "ERG", /* 22 t: erase gap */ + "","","","","","","","","", /* 23-31 */ + "CMP", /* 32 b: compare */ + "RD", /* 33 b: read */ + "WR", /* 34 b: write */ + "", /* 35 */ + "WTM", /* 36 t: write tape mark */ + "POS", /* 37 t: reposition */ + "","","","","","","","","", /* 38-46 */ + "FMT", /* 47 d: format */ + "","","","","","","","","","","","","","","","", /* 48-63 */ + "AVA", /* 64 b: unit now avail */ + }; + /* Forward references */ DEVICE tq_dev; @@ -369,12 +414,12 @@ DIB tq_dib = { }; UNIT tq_unit[] = { - { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, - { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, - { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, - { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, - { UDATA (&tq_tmrsvc, UNIT_DIS, 0) }, - { UDATA (&tq_quesvc, UNIT_DIS, 0) } + { UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE, 0), INIT_CAP }, + { UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE, 0), INIT_CAP }, + { UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE, 0), INIT_CAP }, + { UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE, 0), INIT_CAP }, + { UDATA (&tq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0) }, + { UDATA (&tq_quesvc, UNIT_IDLE|UNIT_DIS, 0) } }; #define TQ_TIMER (TQ_NUMDR) @@ -411,6 +456,7 @@ REG tq_reg[] = { { DRDATA (I4TIME, tq_itime4, 24), PV_LEFT + REG_NZ }, { DRDATA (QTIME, tq_qtime, 24), PV_LEFT + REG_NZ }, { DRDATA (XTIME, tq_xtime, 24), PV_LEFT + REG_NZ }, + { DRDATA (RWTIME, tq_rwtime, 32), PV_LEFT + REG_NZ }, { BRDATA (PKTS, tq_pkt, DEV_RDX, 16, TQ_NPKTS * (TQ_PKT_SIZE_W + 1)) }, { DRDATA (DEVTYPE, tq_typ, 2), REG_HRO }, { DRDATA (DEVCAP, drv_tab[TQU_TYPE].cap, T_ADDR_W), PV_LEFT | REG_HRO }, @@ -466,10 +512,10 @@ DEVICE tq_dev = { &tq_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG }; -/* I/O dispatch routines, I/O addresses 17772150 - 17772152 +/* I/O dispatch routines, I/O addresses 17774500 - 17774502 - 17772150 IP read/write - 17772152 SA read/write + 17774500 IP read/write + 17774502 SA read/write */ t_stat tq_rd (int32 *data, int32 PA, int32 access) @@ -643,13 +689,16 @@ if ((pkt == 0) && tq_pip) { /* polling? */ if (pkt) { /* got one? */ if (DEBUG_PRS (tq_dev)) { UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]); - fprintf (sim_deb, ">>TQ: cmd=%04X, mod=%04X, unit=%d, ", - tq_pkt[pkt].d[CMD_OPC], tq_pkt[pkt].d[CMD_MOD], tq_pkt[pkt].d[CMD_UN]); + fprintf (sim_deb, ">>TQ: cmd=%04X(%3s), mod=%04X, unit=%d, ", + tq_pkt[pkt].d[CMD_OPC], tq_cmdname[tq_pkt[pkt].d[CMD_OPC]&0x3f], tq_pkt[pkt].d[CMD_MOD], tq_pkt[pkt].d[CMD_UN]); fprintf (sim_deb, "bc=%04X%04X, ma=%04X%04X", tq_pkt[pkt].d[RW_BCH], tq_pkt[pkt].d[RW_BCL], tq_pkt[pkt].d[RW_BAH], tq_pkt[pkt].d[RW_BAL]); - if (up) - fprintf (sim_deb, ", pos=%d, obj=%d\n", up->pos, up->objp); + if (up) { + fprintf (sim_deb, ", pos="); + fprint_val (sim_deb, up->pos, 10, T_ADDR_W, PV_LEFT); + fprintf (sim_deb, ", obj=%d\n", up->objp); + } else fprintf (sim_deb, "\n"); fflush (sim_deb); } @@ -734,7 +783,7 @@ else { /* valid cmd */ } /* if (tq_cmf[cmd] & MD_CDL) /* clr cch lost? */ /* uptr->flags = uptr->flags & ~UNIT_CDL; */ - if (tq_cmf[cmd] & MD_CSE) /* clr ser exc? */ + if ((mdf & MD_CSE) && (uptr->flags & UNIT_SXC)) /* clr ser exc? */ uptr->flags = uptr->flags & ~UNIT_SXC; } switch (cmd) { @@ -847,19 +896,19 @@ uint32 sts; UNIT *uptr; if (uptr = tq_getucb (lu)) { /* unit exist? */ - if (uptr->flags & UNIT_SXC) /* ser exc pending? */ + if (uptr->flags & UNIT_SXC) /* ser exc pending? */ sts = ST_SXC; - else { - uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_TMK | UNIT_POL); - sim_tape_rewind (uptr); /* rewind */ - uptr->uf = uptr->objp = 0; /* clr flags */ - if (uptr->flags & UNIT_ATT) { /* attached? */ - sts = ST_SUC; /* success */ - if (mdf & MD_UNL) /* unload? */ + else { + uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_TMK | UNIT_POL); + sim_tape_rewind (uptr); /* rewind */ + uptr->uf = uptr->objp = 0; /* clr flags */ + if (uptr->flags & UNIT_ATT) { /* attached? */ + sts = ST_SUC; /* success */ + if (mdf & MD_UNL) /* unload? */ tq_detach (uptr); - } - else sts = ST_OFL | SB_OFL_NV; /* no, offline */ - } + } + else sts = ST_OFL | SB_OFL_NV; /* no, offline */ + } } else sts = ST_OFL; /* offline */ tq_putr (pkt, OP_AVL | OP_END, tq_efl (uptr), sts, AVL_LNT, UQ_TYP_SEQ); @@ -1066,7 +1115,12 @@ if (uptr = tq_getucb (lu)) { /* unit exist? */ sts = tq_mot_valid (uptr, OP_POS); /* validity checks */ if (sts == ST_SUC) { /* ok? */ uptr->cpkt = pkt; /* op in progress */ - sim_activate (uptr, tq_xtime); /* activate */ + tq_rwtime = 2 * tmr_poll * clk_tps; /* 2 second rewind time */ + if ((tq_pkt[pkt].d[CMD_MOD] & MD_RWD) && /* rewind? */ + (!(tq_pkt[pkt].d[CMD_MOD] & MD_IMM))) /* !immediate? */ + sim_activate (uptr, tq_rwtime); /* use 2 sec rewind execute time */ + else /* otherwise */ + sim_activate (uptr, tq_xtime); /* use normal execute time */ return OK; /* done */ } } @@ -1180,6 +1234,8 @@ switch (cmd) { /* case on command */ return tq_mot_err (uptr, tbc); /* log, done */ } if ((sts != ST_SUC) || (cmd == OP_ACC)) { /* error or access? */ + if (sts == ST_TMK) + uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */ break; } @@ -1286,6 +1342,11 @@ switch (cmd) { /* case on command */ } PUTP32 (pkt, POS_RCL, skrec); /* #rec skipped */ PUTP32 (pkt, POS_TMCL, sktmk); /* #tmk skipped */ + if (DEBUG_PRS (tq_dev)) { + fprintf (sim_deb, ">>TQ: Position Done: mdf=%04X, nrec=%04X, ntmk=%04X, skrec=%04X, sktmk=%04X\n", + mdf, nrec, ntmk, skrec, sktmk); + fflush (sim_deb); + } break; default: @@ -1690,8 +1751,11 @@ if (DEBUG_PRS (tq_dev)) { UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]); fprintf (sim_deb, ">>TQ: rsp=%04X, sts=%04X", tq_pkt[pkt].d[RSP_OPF], tq_pkt[pkt].d[RSP_STS]); - if (up) - fprintf (sim_deb, ", pos=%d, obj=%d\n", up->pos, up->objp); + if (up) { + fprintf (sim_deb, ", pos="); + fprint_val (sim_deb, up->pos, 10, T_ADDR_W, PV_LEFT); + fprintf (sim_deb, ", obj=%d\n", up->objp); + } else fprintf (sim_deb, "\n"); fflush (sim_deb); } @@ -1816,6 +1880,7 @@ void tq_putr_unit (int32 pkt, UNIT *uptr, uint32 lu, t_bool all) { tq_pkt[pkt].d[ONL_MLUN] = lu; /* multi-unit */ tq_pkt[pkt].d[ONL_UFL] = uptr->uf | TQ_WPH (uptr); /* unit flags */ +tq_pkt[pkt].d[ONL_UFL] |= tq_efl (uptr); /* end flags accordingly */ tq_pkt[pkt].d[ONL_RSVL] = tq_pkt[pkt].d[ONL_RSVH] = 0; /* reserved */ tq_pkt[pkt].d[ONL_UIDA] = lu; /* UID low */ tq_pkt[pkt].d[ONL_UIDB] = 0; diff --git a/PDP11/pdp11_ts.c b/PDP11/pdp11_ts.c index 6aae9e28..01a5b0fe 100644 --- a/PDP11/pdp11_ts.c +++ b/PDP11/pdp11_ts.c @@ -1,6 +1,6 @@ /* pdp11_ts.c: TS11/TSV05 magnetic tape simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,8 @@ ts TS11/TSV05 magtape + 22-May-10 RMS Fixed t_addr printouts for 64b big-endian systems + (found by Mark Pizzolato) 16-Feb-06 RMS Added tape capacity checking 31-Oct-05 RMS Fixed address width for large files 16-Aug-05 RMS Fixed C++ declaration and cast problems @@ -733,9 +735,12 @@ if (!(cmdhdr & CMD_ACK)) { /* no acknowledge? */ } fnc = GET_FNC (cmdhdr); /* get fnc+mode */ mod = GET_MOD (cmdhdr); -if (DEBUG_PRS (ts_dev)) - fprintf (sim_deb, ">>TS: cmd=%s, mod=%o, buf=%o, lnt=%d, pos=%d\n", - fnc_name[fnc], mod, cmdadl, cmdlnt, ts_unit.pos); +if (DEBUG_PRS (ts_dev)) { + fprintf (sim_deb, ">>TS: cmd=%s, mod=%o, buf=%o, lnt=%d, pos=", + fnc_name[fnc], mod, cmdadl, cmdlnt); + fprint_val (sim_deb, ts_unit.pos, 10, T_ADDR_W, PV_LEFT); + fprintf (sim_deb, "\n"); + } if ((fnc != FNC_WCHR) && (tssr & TSSR_NBA)) { /* ~wr chr & nba? */ ts_endcmd (TC3, 0, 0); /* error */ return SCPE_OK; @@ -1016,9 +1021,12 @@ tssr = ts_updtssr (tssr | tc | TSSR_SSR | (tc? TSSR_SC: 0)); if (cmdhdr & CMD_IE) SET_INT (TS); ts_ownm = 0; ts_ownc = 0; -if (DEBUG_PRS (ts_dev)) - fprintf (sim_deb, ">>TS: sta=%o, tc=%o, rfc=%d, pos=%d\n", - msgxs0, GET_TC (tssr), msgrfc, ts_unit.pos); +if (DEBUG_PRS (ts_dev)) { + fprintf (sim_deb, ">>TS: sta=%o, tc=%o, rfc=%d, pos=", + msgxs0, GET_TC (tssr), msgrfc); + fprint_val (sim_deb, ts_unit.pos, 10, T_ADDR_W, PV_LEFT); + fprintf (sim_deb, "\n"); + } return; } diff --git a/PDP11/pdp11_xq.c b/PDP11/pdp11_xq.c index 06ef780f..89591bad 100644 --- a/PDP11/pdp11_xq.c +++ b/PDP11/pdp11_xq.c @@ -26,11 +26,12 @@ ------------------------------------------------------------------------------ - This DEQNA/DELQA simulation is based on: + This DEQNA/DELQA/DELQA-T simulation is based on: Digital DELQA Users Guide, Part# EK-DELQA-UG-002 Digital DEQNA Users Guide, Part# EK-DEQNA-UG-001 + Digital DELQA-Plus Addendum to DELQA Users Guide, Part# EK-DELQP-UG-001_Sep89.pdf These manuals can be found online at: - http://www.spies.com/~aek/pdf/dec/qbus + http://www.bitsavers.org/pdf/dec/qbus Certain adaptations have been made because this is an emulation: Ethernet transceiver power flag CSR<12> is ON when attached. @@ -58,6 +59,7 @@ 5. VMS LAT - SET HOST/LAT tests 6. VMS Cluster - SHOW CLUSTER, SHOW DEVICE, and cluster COPY tests 7. Console boot into VMSCluster (>>>B XQAO) + 8. Console DELQA Diagnostic (>>>TEST 82) PDP11: 1. RT-11 v5.3 - FTPSB copy test 2. RSTS/E v10.1 - detects/enables device @@ -66,6 +68,42 @@ Modification history: + 09-Dec-10 MP Added address conflict check during attach. + 06-Dec-10 MP Fixed loopback processing to correctly handle forward packets. + 29-Nov-10 MP Fixed interrupt dispatch issue which caused delivered packets + (in and out) to sometimes not interrupt the CPU after processing. + 07-Mar-08 MP Fixed the SCP visibile SA registers to always display the + ROM mac address, even after it is changed by SET XQ MAC=. + 07-Mar-08 MP Added changes so that the Console DELQA diagnostic (>>>TEST 82) + will succeed. + 03-Mar-08 MP Added DELQA-T (aka DELQA Plus) device emulation support. + 06-Feb-08 MP Added dropped frame statistics to record when the receiver discards + received packets due to the receiver being disabled, or due to the + XQ device's packet receive queue being full. + Fixed bug in receive processing when we're not polling. This could + cause receive processing to never be activated again if we don't + read all available packets via eth_read each time we get the + opportunity. + 31-Jan-08 MP Added the ability to Coalesce received packet interrupts. This + is enabled by SET XQ POLL=DELAY=nnn where nnn is a number of + microseconds to delay the triggering of an interrupt when a packet + is received. + 29-Jan-08 MP Added SET XQ POLL=DISABLE (aka SET XQ POLL=0) to operate without + polling for packet read completion. + 29-Jan-08 MP Changed the sanity and id timer mechanisms to use a separate timer + unit so that transmit and recieve activities can be dealt with + by the normal xq_svc routine. + Dynamically determine the timer polling rate based on the + calibrated tmr_poll and clk_tps values of the simulator. + 25-Jan-08 MP Enabled the SET XQ POLL to be meaningful if the simulator currently + doesn't support idling. + 25-Jan-08 MP Changed xq_debug_setup to use sim_debug instead of printf so that + all debug output goes to the same place. + 25-Jan-08 MP Restored the call to xq_svc after all successful calls to eth_write + to allow receive processing to happen before the next event + service time. This must have been inadvertently commented out + while other things were being tested. + 23-Jan-08 MP Added debugging support to display packet headers and packet data 18-Jun-07 RMS Added UNIT_IDLE flag 29-Oct-06 RMS Synced poll and clock 27-Jan-06 RMS Fixed unaligned accesses in XQB (found by Doug Carman) @@ -90,7 +128,7 @@ device in promiscuous or all multicast mode once it ever had been there. - Fixed output format in show_xq_sanity to end in "\n" - - Added display of All Multicase and promiscuous to + - Added display of All Multicast and promiscuous to xq_show_filters - The stuck in All Multicast or Promiscuous issue is worse than previously thought. See comments in @@ -99,7 +137,7 @@ separator character, since sim_ether's eth_mac_fmt formats them with this separator character. - Changed xq_sw_reset to behave more like the set of - actions described in Table 3-6 of the DELQA manua. + actions described in Table 3-6 of the DELQA manual. The manual mentions "N/A" which I'm interpreting to mean "Not Affected". 05-Jun-03 DTH Added receive packet splitting @@ -212,13 +250,17 @@ #include "pdp11_xq_bootrom.h" extern int32 tmxr_poll; +extern int32 tmr_poll, clk_tps; +extern t_bool sim_idle_enab; extern FILE* sim_deb; +extern FILE *sim_log; extern char* read_line (char *ptr, int32 size, FILE *stream); /* forward declarations */ t_stat xq_rd(int32* data, int32 PA, int32 access); t_stat xq_wr(int32 data, int32 PA, int32 access); t_stat xq_svc(UNIT * uptr); +t_stat xq_tmrsvc(UNIT * uptr); t_stat xq_reset (DEVICE * dptr); t_stat xq_attach (UNIT * uptr, char * cptr); t_stat xq_detach (UNIT * uptr); @@ -235,7 +277,10 @@ t_stat xq_show_poll (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xq_set_poll (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat xq_process_xbdl(CTLR* xq); t_stat xq_dispatch_xbdl(CTLR* xq); -void xq_start_receiver(void); +t_stat xq_process_turbo_rbdl(CTLR* xq); +t_stat xq_process_turbo_xbdl(CTLR* xq); +void xq_start_receiver(CTLR* xq); +void xq_stop_receiver(CTLR* xq); void xq_sw_reset(CTLR* xq); t_stat xq_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat xq_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); @@ -255,8 +300,10 @@ struct xq_device xqa = { xqa_read_callback, /* read callback routine */ xqa_write_callback, /* write callback routine */ {0x08, 0x00, 0x2B, 0xAA, 0xBB, 0xCC}, /* mac */ - XQ_T_DELQA, /* type */ + XQ_T_DELQA_PLUS, /* type */ + XQ_T_DELQA, /* mode */ XQ_SERVICE_INTERVAL, /* poll */ + 0, 0, /* coalesce */ {0} /* sanity */ }; @@ -264,8 +311,10 @@ struct xq_device xqb = { xqb_read_callback, /* read callback routine */ xqb_write_callback, /* write callback routine */ {0x08, 0x00, 0x2B, 0xBB, 0xCC, 0xDD}, /* mac */ - XQ_T_DELQA, /* type */ + XQ_T_DELQA_PLUS, /* type */ + XQ_T_DELQA, /* mode */ XQ_SERVICE_INTERVAL, /* poll */ + 0, 0, /* coalesce */ {0} /* sanity */ }; @@ -275,15 +324,18 @@ DIB xqa_dib = { IOBA_XQ, IOLN_XQ, &xq_rd, &xq_wr, UNIT xqa_unit[] = { { UDATA (&xq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 2047) }, /* receive timer */ + { UDATA (&xq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0) }, }; REG xqa_reg[] = { - { GRDATA ( SA0, xqa.addr[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA1, xqa.addr[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA2, xqa.addr[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA3, xqa.addr[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA4, xqa.addr[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA5, xqa.addr[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA0, xqa.mac[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA1, xqa.mac[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA2, xqa.mac[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA3, xqa.mac[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA4, xqa.mac[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA5, xqa.mac[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( MX0, xqa.mac_checksum[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( MX1, xqa.mac_checksum[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( RBDL, xqa.rbdl[0], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( RBDH, xqa.rbdl[1], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( XBDL, xqa.xbdl[0], XQ_RDX, 16, 0), REG_FIT }, @@ -291,6 +343,13 @@ REG xqa_reg[] = { { GRDATA ( VAR, xqa.var, XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( CSR, xqa.csr, XQ_RDX, 16, 0), REG_FIT }, { FLDATA ( INT, xqa.irq, 0) }, + { GRDATA ( TYPE, xqa.type, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA ( MODE, xqa.mode, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA ( POLL, xqa.poll, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA ( CLAT, xqa.coalesce_latency, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA ( CLATT, xqa.coalesce_latency_ticks, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA ( RBDL_BA, xqa.rbdl_ba, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( XBDL_BA, xqa.xbdl_ba, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_PRM, xqa.setup.promiscuous, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_MLT, xqa.setup.multicast, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_L1, xqa.setup.l1, XQ_RDX, 32, 0), REG_HRO}, @@ -298,6 +357,17 @@ REG xqa_reg[] = { { GRDATA ( SETUP_L3, xqa.setup.l3, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_SAN, xqa.setup.sanity_timer, XQ_RDX, 32, 0), REG_HRO}, { BRDATA ( SETUP_MACS, &xqa.setup.macs, XQ_RDX, 8, sizeof(xqa.setup.macs)), REG_HRO}, + { BRDATA ( STATS, &xqa.stats, XQ_RDX, 8, sizeof(xqa.setup.macs)), REG_HRO}, + { BRDATA ( TURBO_INIT, &xqa.init, XQ_RDX, 8, sizeof(xqa.init)), REG_HRO}, + { GRDATA ( SRR, xqa.srr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( SRQR, xqa.srqr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( IBA, xqa.iba, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA ( ICR, xqa.icr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( IPEND, xqa.pending_interrupt, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( TBINDX, xqa.tbindx, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( RBINDX, xqa.rbindx, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( IDTMR, xqa.idtmr, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( MUST_POLL, xqa.must_poll, XQ_RDX, 32, 0), REG_HRO}, { NULL }, }; @@ -306,15 +376,18 @@ DIB xqb_dib = { IOBA_XQB, IOLN_XQB, &xq_rd, &xq_wr, UNIT xqb_unit[] = { { UDATA (&xq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 2047) }, /* receive timer */ + { UDATA (&xq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0) }, }; REG xqb_reg[] = { - { GRDATA ( SA0, xqb.addr[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA1, xqb.addr[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA2, xqb.addr[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA3, xqb.addr[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA4, xqb.addr[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, - { GRDATA ( SA5, xqb.addr[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA0, xqb.mac[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA1, xqb.mac[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA2, xqb.mac[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA3, xqb.mac[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA4, xqb.mac[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA5, xqb.mac[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( MX0, xqb.mac_checksum[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( MX1, xqb.mac_checksum[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( RBDL, xqb.rbdl[0], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( RBDH, xqb.rbdl[1], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( XBDL, xqb.xbdl[0], XQ_RDX, 16, 0), REG_FIT }, @@ -322,6 +395,13 @@ REG xqb_reg[] = { { GRDATA ( VAR, xqb.var, XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( CSR, xqb.csr, XQ_RDX, 16, 0), REG_FIT }, { FLDATA ( INT, xqb.irq, 0) }, + { GRDATA ( TYPE, xqb.type, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA ( MODE, xqb.mode, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA ( POLL, xqb.poll, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA ( CLAT, xqb.coalesce_latency, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA ( CLATT, xqb.coalesce_latency_ticks, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA ( RBDL_BA, xqb.rbdl_ba, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( XBDL_BA, xqb.xbdl_ba, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_PRM, xqb.setup.promiscuous, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_MLT, xqb.setup.multicast, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_L1, xqb.setup.l1, XQ_RDX, 32, 0), REG_HRO}, @@ -329,6 +409,17 @@ REG xqb_reg[] = { { GRDATA ( SETUP_L3, xqb.setup.l3, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_SAN, xqb.setup.sanity_timer, XQ_RDX, 32, 0), REG_HRO}, { BRDATA ( SETUP_MACS, &xqb.setup.macs, XQ_RDX, 8, sizeof(xqb.setup.macs)), REG_HRO}, + { BRDATA ( STATS, &xqb.stats, XQ_RDX, 8, sizeof(xqa.setup.macs)), REG_HRO}, + { BRDATA ( TURBO_INIT, &xqb.init, XQ_RDX, 8, sizeof(xqb.init)), REG_HRO}, + { GRDATA ( SRR, xqb.srr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( SRQR, xqb.srqr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( IBA, xqb.iba, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA ( ICR, xqb.icr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( IPEND, xqb.pending_interrupt, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( TBINDX, xqb.tbindx, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( RBINDX, xqb.rbindx, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( IDTMR, xqb.idtmr, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( MUST_POLL, xqb.must_poll, XQ_RDX, 32, 0), REG_HRO}, { NULL }, }; @@ -345,10 +436,15 @@ MTAB xq_mod[] = { NULL, &xq_show_filters, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATS", "STATS", &xq_set_stats, &xq_show_stats, NULL }, - { MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE={DEQNA|DELQA}", + { MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE={DEQNA|DELQA|DELQA-T}", &xq_set_type, &xq_show_type, NULL }, - { MTAB_XTD | MTAB_VDV, 0, "POLL", "POLL={DEFAULT|4..2500]", +#ifdef USE_READER_THREAD + { MTAB_XTD | MTAB_VDV, 0, "POLL", "POLL={DEFAULT|DISABLED|4..2500|DELAY=nnn}", &xq_set_poll, &xq_show_poll, NULL }, +#else + { MTAB_XTD | MTAB_VDV, 0, "POLL", "POLL={DEFAULT|DISABLED|4..2500}", + &xq_set_poll, &xq_show_poll, NULL }, +#endif { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "SANITY", "SANITY={ON|OFF}", &xq_set_sanity, &xq_show_sanity, NULL }, { 0 }, @@ -363,13 +459,14 @@ DEBTAB xq_debug[] = { {"SANITY", DBG_SAN}, {"REG", DBG_REG}, {"PACKET", DBG_PCK}, + {"DATA", DBG_DAT}, {"ETH", DBG_ETH}, {0} }; DEVICE xq_dev = { "XQ", xqa_unit, xqa_reg, xq_mod, - 1, XQ_RDX, 11, 1, XQ_RDX, 16, + 2, XQ_RDX, 11, 1, XQ_RDX, 16, &xq_ex, &xq_dep, &xq_reset, NULL, &xq_attach, &xq_detach, &xqa_dib, DEV_DISABLE | DEV_QBUS | DEV_DEBUG, @@ -378,7 +475,7 @@ DEVICE xq_dev = { DEVICE xqb_dev = { "XQB", xqb_unit, xqb_reg, xq_mod, - 1, XQ_RDX, 11, 1, XQ_RDX, 16, + 2, XQ_RDX, 11, 1, XQ_RDX, 16, &xq_ex, &xq_dep, &xq_reset, NULL, &xq_attach, &xq_detach, &xqb_dib, DEV_DISABLE | DEV_DIS | DEV_QBUS | DEV_DEBUG, @@ -394,8 +491,16 @@ const char* const xq_recv_regnames[] = { "MAC0", "MAC1", "MAC2", "MAC3", "MAC4", "MAC5", "VAR", "CSR" }; +const char* const xqt_recv_regnames[] = { + "MAC0", "MAC1", "MAC2", "MAC3", "MAC4", "MAC5", "SRR", "" +}; + const char* const xq_xmit_regnames[] = { - "", "", "RBDL-Lo", "RBDL-Hi", "XBDL-Lo", "XBDL-Hi", "VAR", "CSR" + "XCR0", "XCR1", "RBDL-Lo", "RBDL-Hi", "XBDL-Lo", "XBDL-Hi", "VAR", "CSR" +}; + +const char* const xqt_xmit_regnames[] = { + "IBAL", "IBAH", "ICR", "", "SRQR", "", "", "ARQR" }; const char* const xq_csr_bits[] = { @@ -408,8 +513,14 @@ const char* const xq_var_bits[] = { "V6", "V7", "S1", "S2", "S3", "RS", "OS", "MS" }; +const char* const xq_srr_bits[] = { + "RS0", "RS1", "", "", "", "", "", "", + "", "TBL", "IME", "PAR", "NXM", "", "CHN", "FES" +}; + /* internal debugging routines */ void xq_debug_setup(CTLR* xq); +void xq_debug_turbo_setup(CTLR* xq); /*============================================================================*/ @@ -543,16 +654,19 @@ t_stat xq_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc) char* fmt = " %-15s%d\n"; CTLR* xq = xq_unit2ctlr(uptr); - fprintf(st, "Ethernet statistics:\n"); - fprintf(st, fmt, "Recv:", xq->var->stats.recv); - fprintf(st, fmt, "Filtered:", xq->var->stats.filter); - fprintf(st, fmt, "Xmit:", xq->var->stats.xmit); - fprintf(st, fmt, "Xmit Fail:", xq->var->stats.fail); - fprintf(st, fmt, "Runts:", xq->var->stats.runt); - fprintf(st, fmt, "Oversize:", xq->var->stats.giant); - fprintf(st, fmt, "Setup:", xq->var->stats.setup); - fprintf(st, fmt, "Loopback:", xq->var->stats.loop); - fprintf(st, fmt, "ReadQ high:", xq->var->ReadQ.high); + fprintf(st, "XQ Ethernet statistics:\n"); + fprintf(st, fmt, "Recv:", xq->var->stats.recv); + fprintf(st, fmt, "Dropped:", xq->var->stats.dropped + xq->var->ReadQ.loss); + fprintf(st, fmt, "Xmit:", xq->var->stats.xmit); + fprintf(st, fmt, "Xmit Fail:", xq->var->stats.fail); + fprintf(st, fmt, "Runts:", xq->var->stats.runt); + fprintf(st, fmt, "Oversize:", xq->var->stats.giant); + fprintf(st, fmt, "SW Reset:", xq->var->stats.reset); + fprintf(st, fmt, "Setup:", xq->var->stats.setup); + fprintf(st, fmt, "Loopback:", xq->var->stats.loop); + fprintf(st, fmt, "ReadQ count:", xq->var->ReadQ.count); + fprintf(st, fmt, "ReadQ high:", xq->var->ReadQ.high); + eth_show_dev(st, xq->var->etherface); return SCPE_OK; } @@ -562,15 +676,28 @@ t_stat xq_show_filters (FILE* st, UNIT* uptr, int32 val, void* desc) char buffer[20]; int i; - fprintf(st, "Filters:\n"); - for (i=0; ivar->setup.macs[i], buffer); - fprintf(st, " [%2d]: %s\n", i, buffer); - }; - if (xq->var->setup.multicast) - fprintf(st, "All Multicast Receive Mode\n"); - if (xq->var->setup.promiscuous) - fprintf(st, "Promiscuous Receive Mode\n"); + if (xq->var->mode == XQ_T_DELQA_PLUS) { + eth_mac_fmt(&xq->var->init.phys, buffer); + fprintf(st, "Physical Address=%s\n", buffer); + if (xq->var->etherface->hash_filter) { + fprintf(st, "Multicast Hash: "); + for (i=0; ivar->etherface->hash); ++i) + fprintf(st, "%02X ", xq->var->etherface->hash[i]); + fprintf(st, "\n"); + } + if (xq->var->init.mode & XQ_IN_MO_PRO) + fprintf(st, "Promiscuous Receive Mode\n"); + } else { + fprintf(st, "Filters:\n"); + for (i=0; ivar->setup.macs[i], buffer); + fprintf(st, " [%2d]: %s\n", i, buffer); + } + if (xq->var->setup.multicast) + fprintf(st, "All Multicast Receive Mode\n"); + if (xq->var->setup.promiscuous) + fprintf(st, "Promiscuous Receive Mode\n"); + } return SCPE_OK; } @@ -581,6 +708,15 @@ t_stat xq_show_type (FILE* st, UNIT* uptr, int32 val, void* desc) switch (xq->var->type) { case XQ_T_DEQNA: fprintf(st, "DEQNA"); break; case XQ_T_DELQA: fprintf(st, "DELQA"); break; + case XQ_T_DELQA_PLUS: fprintf(st, "DELQA-T"); break; + } + if (xq->var->type != xq->var->mode) { + fprintf(st, ",mode="); + switch (xq->var->mode) { + case XQ_T_DEQNA: fprintf(st, "DEQNA"); break; + case XQ_T_DELQA: fprintf(st, "DELQA"); break; + case XQ_T_DELQA_PLUS: fprintf(st, "DELQA-T"); break; + } } return SCPE_OK; } @@ -594,7 +730,11 @@ t_stat xq_set_type (UNIT* uptr, int32 val, char* cptr, void* desc) /* this assumes that the parameter has already been upcased */ if (!strcmp(cptr, "DEQNA")) xq->var->type = XQ_T_DEQNA; else if (!strcmp(cptr, "DELQA")) xq->var->type = XQ_T_DELQA; + else if (!strcmp(cptr, "DELQA-T")) xq->var->type = XQ_T_DELQA_PLUS; else return SCPE_ARG; + xq->var->mode = XQ_T_DELQA; + if (xq->var->type == XQ_T_DEQNA) + xq->var->mode = XQ_T_DEQNA; return SCPE_OK; } @@ -602,7 +742,13 @@ t_stat xq_set_type (UNIT* uptr, int32 val, char* cptr, void* desc) t_stat xq_show_poll (FILE* st, UNIT* uptr, int32 val, void* desc) { CTLR* xq = xq_unit2ctlr(uptr); - fprintf(st, "poll=%d", xq->var->poll); + if (xq->var->poll) + fprintf(st, "poll=%d", xq->var->poll); + else { + fprintf(st, "polling=disabled"); + if (xq->var->coalesce_latency) + fprintf(st, ",latency=%d", xq->var->coalesce_latency); + } return SCPE_OK; } @@ -615,10 +761,22 @@ t_stat xq_set_poll (UNIT* uptr, int32 val, char* cptr, void* desc) /* this assumes that the parameter has already been upcased */ if (!strcmp(cptr, "DEFAULT")) xq->var->poll = XQ_SERVICE_INTERVAL; + else if ((!strcmp(cptr, "DISABLED")) || (!strncmp(cptr, "DELAY=", 6))) { + xq->var->poll = 0; + if (!strncmp(cptr, "DELAY=", 6)) { + int delay = 0; + if (1 != sscanf(cptr+6, "%d", &delay)) + return SCPE_ARG; + xq->var->coalesce_latency = delay; + xq->var->coalesce_latency_ticks = (tmr_poll * clk_tps * xq->var->coalesce_latency) / 1000000; + } + } else { int newpoll = 0; - sscanf(cptr, "%d", &newpoll); - if ((newpoll >= 4) && (newpoll <= 2500)) + if (1 != sscanf(cptr, "%d", &newpoll)) + return SCPE_ARG; + if ((newpoll == 0) || + ((!sim_idle_enab) && (newpoll >= 4) && (newpoll <= 2500))) xq->var->poll = newpoll; else return SCPE_ARG; @@ -660,8 +818,13 @@ t_stat xq_nxm_error(CTLR* xq) const uint16 set_bits = XQ_CSR_NI | XQ_CSR_XI | XQ_CSR_XL | XQ_CSR_RL; sim_debug(DBG_WRN, xq->dev, "Non Existent Memory Error!\n"); - /* set NXM and associated bits in CSR */ - xq_csr_set_clr(xq, set_bits , 0); + if (xq->var->mode == XQ_T_DELQA_PLUS) { + /* set NXM and associated bits in SRR */ + xq->var->srr |= (XQ_SRR_FES | XQ_SRR_NXM); + xq_setint(xq); + } else + /* set NXM and associated bits in CSR */ + xq_csr_set_clr(xq, set_bits , 0); return SCPE_OK; } @@ -670,7 +833,6 @@ t_stat xq_nxm_error(CTLR* xq) */ void xq_write_callback (CTLR* xq, int status) { - t_stat rstatus; int32 wstatus; const uint16 TDR = 100 + xq->var->write_buffer.len * 8; /* arbitrary value */ uint16 write_success[2] = {0}; @@ -681,6 +843,8 @@ void xq_write_callback (CTLR* xq, int status) xq->var->stats.xmit += 1; /* update write status words */ if (status == 0) { /* success */ + if (DBG_PCK & xq->dev->dctrl) + eth_packet_trace_ex(xq->var->etherface, xq->var->write_buffer.msg, xq->var->write_buffer.len, "xq-write", DBG_DAT & xq->dev->dctrl, DBG_PCK); wstatus = Map_WriteW(xq->var->xbdl_ba + 8, 4, write_success); } else { /* failure */ sim_debug(DBG_WRN, xq->dev, "Packet Write Error!\n"); @@ -701,11 +865,6 @@ void xq_write_callback (CTLR* xq, int status) /* clear write buffer */ xq->var->write_buffer.len = 0; - /* next descriptor (implicit) */ - xq->var->xbdl_ba += 12; - - /* finish processing xbdl */ - rstatus = xq_process_xbdl(xq); } void xqa_write_callback (int status) @@ -724,7 +883,7 @@ t_stat xq_rd(int32* data, int32 PA, int32 access) CTLR* xq = xq_pa2ctlr(PA); int index = (PA >> 1) & 07; /* word index */ - sim_debug(DBG_REG, xq->dev, "xq_rd(PA=0x%08X [%s], access=%d)\n", PA, xq_recv_regnames[index], access); + sim_debug(DBG_REG, xq->dev, "xq_rd(PA=0x%08X [%s], access=%d)\n", PA, ((xq->var->mode == XQ_T_DELQA_PLUS) ? xqt_recv_regnames[index] : xq_recv_regnames[index]), access); switch (index) { case 0: case 1: @@ -741,9 +900,14 @@ t_stat xq_rd(int32* data, int32 PA, int32 access) *data = 0xFF00 | xq->var->mac[index]; break; case 6: - sim_debug_u16(DBG_VAR, xq->dev, xq_var_bits, xq->var->var, xq->var->var, 0); - sim_debug (DBG_VAR, xq->dev, ", vec = 0%o\n", (xq->var->var & XQ_VEC_IV)); - *data = xq->var->var; + if (xq->var->mode != XQ_T_DELQA_PLUS) { + sim_debug_u16(DBG_VAR, xq->dev, xq_var_bits, xq->var->var, xq->var->var, 0); + sim_debug (DBG_VAR, xq->dev, ", vec = 0%o\n", (xq->var->var & XQ_VEC_IV)); + *data = xq->var->var; + } else { + sim_debug_u16(DBG_VAR, xq->dev, xq_srr_bits, xq->var->srr, xq->var->srr, 0); + *data = xq->var->srr; + } break; case 7: sim_debug_u16(DBG_CSR, xq->dev, xq_csr_bits, xq->var->csr, xq->var->csr, 1); @@ -765,6 +929,9 @@ t_stat xq_process_rbdl(CTLR* xq) ETH_ITEM* item; uint8* rbuf; + if (xq->var->mode == XQ_T_DELQA_PLUS) + return xq_process_turbo_rbdl(xq); + sim_debug(DBG_TRC, xq->dev, "xq_process_rdbl\n"); /* process buffer descriptors */ @@ -868,6 +1035,7 @@ t_stat xq_process_rbdl(CTLR* xq) if (xq->var->ReadQ.loss) { sim_debug(DBG_WRN, xq->dev, "ReadQ overflow!\n"); xq->var->rbdl_buf[4] |= 0x0001; /* set overflow bit */ + xq->var->stats.dropped += xq->var->ReadQ.loss; xq->var->ReadQ.loss = 0; /* reset loss counter */ } @@ -932,6 +1100,13 @@ t_stat xq_process_mop(CTLR* xq) break; case 9: /* Mop Read/Clear Counters */ break; + case 10: /* DELQA-PLUS Board ROM Version */ + if (xq->var->type == XQ_T_DELQA_PLUS) { + uint16 Delqa_Plus_ROM_Version[3] = {2, 0, 0}; /* 2.0.0 */ + wstatus = Map_WriteB(address, sizeof(Delqa_Plus_ROM_Version), (uint8*) Delqa_Plus_ROM_Version); + if (wstatus) return xq_nxm_error(xq); + } + break; } /* switch */ /* process next meb */ @@ -947,11 +1122,16 @@ t_stat xq_process_setup(CTLR* xq) int count = 0; float secs; t_stat status; + uint32 saved_debug = xq->dev->dctrl; ETH_MAC zeros = {0, 0, 0, 0, 0, 0}; ETH_MAC filters[XQ_FILTER_MAX + 1]; sim_debug(DBG_TRC, xq->dev, "xq_process_setup()\n"); + /* temporarily turn on Ethernet debugging if setup debugging is enabled */ + if (xq->dev->dctrl & DBG_SET) + xq->dev->dctrl |= DBG_ETH; + /* extract filter addresses from setup packet */ memset(xq->var->setup.macs, '\0', sizeof(xq->var->setup.macs)); for (i = 0; i < 7; i++) @@ -1018,17 +1198,16 @@ t_stat xq_process_setup(CTLR* xq) case 7: secs = 64 * 60; break; /* 64 minutes */ } xq->var->sanity.quarter_secs = (int) (secs * 4); - xq->var->sanity.max = (int) (secs * xq->var->poll); } /* finalize sanity timer state */ - xq->var->sanity.timer = xq->var->sanity.max; if (xq->var->sanity.enabled != 2) { if (xq->var->csr & XQ_CSR_SE) xq->var->sanity.enabled = 1; else xq->var->sanity.enabled = 0; } + xq_reset_santmr(xq); /* set ethernet filter */ /* memcpy (filters[count++], xq->mac, sizeof(ETH_MAC)); */ @@ -1044,8 +1223,10 @@ t_stat xq_process_setup(CTLR* xq) /* mark setup block valid */ xq->var->setup.valid = 1; - if (sim_deb && (xq->dev->dctrl & DBG_SET)) - xq_debug_setup(xq); + xq_debug_setup(xq); + + xq->dev->dctrl = saved_debug; /* restore original debugging */ + return SCPE_OK; } @@ -1054,6 +1235,9 @@ t_stat xq_process_setup(CTLR* xq) The DELQA manual does not explicitly state whether or not multiple packets can be written in one transmit operation, so a maximum of 1 packet is assumed. + + MP: Hmmm... Figure 3-1 on page 3-3 step 6 says that descriptors will be processed + until the end of the list is found. */ t_stat xq_process_xbdl(CTLR* xq) @@ -1104,7 +1288,7 @@ t_stat xq_process_xbdl(CTLR* xq) /* add to transmit buffer, making sure it's not too big */ if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) - b_length = sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len; + b_length = (uint16)(sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len); rstatus = Map_ReadB(address, b_length, &xq->var->write_buffer.msg[xq->var->write_buffer.len]); if (rstatus) return xq_nxm_error(xq); xq->var->write_buffer.len += b_length; @@ -1145,14 +1329,14 @@ t_stat xq_process_xbdl(CTLR* xq) status = eth_write(xq->var->etherface, &xq->var->write_buffer, xq->var->wcallback); if (status != SCPE_OK) /* not implemented or unattached */ xq_write_callback(xq, 1); /* fake failure */ -#if 0 - else - xq_svc(&xq->unit[0]); /* service any received data */ -#endif + else { + if (xq->var->coalesce_latency == 0) + xq_svc(&xq->unit[0]); /* service any received data */ + } sim_debug(DBG_WRN, xq->dev, "XBDL completed processing write\n"); - return SCPE_OK; } /* loopback/non-loopback */ + } else { /* not at end-of-message */ sim_debug(DBG_WRN, xq->dev, "XBDL processing implicit chain buffer segment\n"); @@ -1230,12 +1414,254 @@ t_stat xq_dispatch_xbdl(CTLR* xq) return status; } +t_stat xq_process_turbo_rbdl(CTLR* xq) +{ + int i; + t_stat status; + int descriptors_consumed = 0; + uint32 rdra = (xq->var->init.rdra_h << 16) | xq->var->init.rdra_l; + + sim_debug(DBG_TRC, xq->dev, "xq_process_turbo_rbdl()\n"); + + if ((xq->var->srr & XQ_SRR_RESP) != XQ_SRR_STRT) + return SCPE_OK; + + /* Process descriptors in the receive ring while the're available and we have packets */ + do { + uint32 address; + uint16 b_length, rbl; + ETH_ITEM* item; + uint8* rbuf; + + /* stop processing when nothing in read queue */ + if (!xq->var->ReadQ.count) + break; + + i = xq->var->rbindx; + + /* Get receive descriptor from memory */ + status = Map_ReadW (rdra+i*sizeof(xq->var->rring[i]), sizeof(xq->var->rring[i]), (uint16 *)&xq->var->rring[i]); + if (status != SCPE_OK) + return xq_nxm_error(xq); + + /* Done if Buffer not Owned */ + if (xq->var->rring[i].rmd3 & XQ_TMD3_OWN) + break; + + ++descriptors_consumed; + + /* Update ring index */ + xq->var->rbindx = (xq->var->rbindx + 1) % XQ_TURBO_RC_BCNT; + + address = ((xq->var->rring[i].hadr & 0x3F ) << 16) | xq->var->rring[i].ladr; + b_length = ETH_FRAME_SIZE; + + item = &xq->var->ReadQ.item[xq->var->ReadQ.head]; + rbl = item->packet.len + ETH_CRC_SIZE; + rbuf = item->packet.msg; + + /* see if packet must be size-adjusted or is splitting */ + if (item->packet.used) { + int used = item->packet.used; + rbl -= used; + rbuf = &item->packet.msg[used]; + } else { + /* adjust runt packets */ + if (rbl < ETH_MIN_PACKET) { + xq->var->stats.runt += 1; + sim_debug(DBG_WRN, xq->dev, "Runt detected, size = %d\n", rbl); + /* pad runts with zeros up to minimum size - this allows "legal" (size - 60) + processing of those weird short ARP packets that seem to occur occasionally */ + memset(&item->packet.msg[rbl], 0, ETH_MIN_PACKET-rbl); + rbl = ETH_MIN_PACKET; + }; + + /* adjust oversized packets */ + if (rbl > ETH_FRAME_SIZE) { + xq->var->stats.giant += 1; + sim_debug(DBG_WRN, xq->dev, "Giant detected, size=%d\n", rbl); + /* trim giants down to maximum size - no documentation on how to handle the data loss */ + item->packet.len = ETH_MAX_PACKET; + rbl = ETH_FRAME_SIZE; + }; + }; + + /* make sure entire packet fits in buffer - if not, will need to split into multiple buffers */ + if (rbl > b_length) + rbl = b_length; + item->packet.used += rbl; + + /* send data to host */ + status = Map_WriteB(address, rbl, rbuf); + if (status != SCPE_OK) + return xq_nxm_error(xq); + + /* set receive size into RBL - RBL<10:8> maps into Status1<10:8>, + RBL<7:0> maps into Status2<7:0>, and Status2<15:8> (copy) */ + xq->var->rring[i].rmd0 = 0; + xq->var->rring[i].rmd1 = rbl; + xq->var->rring[i].rmd2 = XQ_RMD2_RON | XQ_RMD2_TON; + if (0 == (item->packet.used - rbl)) + xq->var->rring[i].rmd0 |= XQ_RMD0_STP; /* Start of Packet */ + if (item->packet.used == (item->packet.len + ETH_CRC_SIZE)) + xq->var->rring[i].rmd0 |= XQ_RMD0_ENP; /* End of Packet */ + + if (xq->var->ReadQ.loss) { + xq->var->rring[i].rmd2 |= XQ_RMD2_MIS; + sim_debug(DBG_WRN, xq->dev, "ReadQ overflow!\n"); + xq->var->stats.dropped += xq->var->ReadQ.loss; + xq->var->ReadQ.loss = 0; /* reset loss counter */ + } + + Map_ReadW (rdra+(uint32)(((char *)(&xq->var->rring[xq->var->rbindx].rmd3))-((char *)&xq->var->rring)), sizeof(xq->var->rring[xq->var->rbindx].rmd3), (uint16 *)&xq->var->rring[xq->var->rbindx].rmd3); + if (xq->var->rring[xq->var->rbindx].rmd3 & XQ_RMD3_OWN) + xq->var->rring[i].rmd2 |= XQ_RMD2_EOR; + + /* Update receive descriptor in memory (only done after we've processed the contents) */ + /* Note: We're updating all but the end of the descriptor (which we never change) */ + /* AND the driver will be allowed to change once the changed tmd3 (ownership) */ + /* is noted so we avoid walking on its changes */ + xq->var->rring[i].rmd3 |= XQ_TMD3_OWN; /* Return Descriptor to Driver */ + status = Map_WriteW (rdra+i*sizeof(xq->var->rring[i]), sizeof(xq->var->rring[i])-8, (uint16 *)&xq->var->rring[i]); + if (status != SCPE_OK) + return xq_nxm_error(xq); + + /* remove packet from queue */ + if (item->packet.used >= item->packet.len) + ethq_remove(&xq->var->ReadQ); + } while (0 == (xq->var->rring[xq->var->rbindx].rmd3 & XQ_RMD3_OWN)); + + if (xq->var->rring[xq->var->rbindx].rmd3 & XQ_RMD3_OWN) + sim_debug(DBG_WRN, xq->dev, "xq_process_turbo_rbdl() - receive ring full\n"); + + if (descriptors_consumed) + /* Interrupt for Packet Reception Completion */ + xq_setint(xq); + + return SCPE_OK; +} + +t_stat xq_process_turbo_xbdl(CTLR* xq) +{ + int i; + t_stat status; + int descriptors_consumed = 0; + uint32 tdra = (xq->var->init.tdra_h << 16) | xq->var->init.tdra_l; + + sim_debug(DBG_TRC, xq->dev, "xq_process_turbo_xbdl()\n"); + + if ((xq->var->srr & XQ_SRR_RESP) != XQ_SRR_STRT) + return SCPE_OK; + + /* clear transmit buffer */ + xq->var->write_buffer.len = 0; + + /* Process each descriptor in the transmit ring */ + do { + uint32 address; + uint16 b_length; + + i = xq->var->tbindx; + + /* Get transmit descriptor from memory */ + status = Map_ReadW (tdra+i*sizeof(xq->var->xring[i]), sizeof(xq->var->xring[i]), (uint16 *)&xq->var->xring[i]); + if (status != SCPE_OK) + return xq_nxm_error(xq); + + if (xq->var->xring[i].tmd3 & XQ_TMD3_OWN) + break; + + /* Update ring index */ + xq->var->tbindx = (xq->var->tbindx + 1) % XQ_TURBO_XM_BCNT; + + ++descriptors_consumed; + address = ((xq->var->xring[i].hadr & 0x3F ) << 16) | xq->var->xring[i].ladr; + b_length = (xq->var->xring[i].tmd3 & XQ_TMD3_BCT); + + /* add to transmit buffer, making sure it's not too big */ + if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) + b_length = (uint16)(sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len); + status = Map_ReadB(address, b_length, &xq->var->write_buffer.msg[xq->var->write_buffer.len]); + if (status != SCPE_OK) + return xq_nxm_error(xq); + + xq->var->write_buffer.len += b_length; + if (!(xq->var->xring[i].tmd3 & XQ_TMD3_FOT)) { + /* Process Loopback if in Loopback mode */ + if (xq->var->init.mode & XQ_IN_MO_LOP) { + if ((xq->var->init.mode & XQ_IN_MO_INT) || (xq->var->etherface)) { + /* put packet in read buffer */ + ethq_insert (&xq->var->ReadQ, 1, &xq->var->write_buffer, 0); + status = SCPE_OK; + } else { + /* External loopback fails when not connected */ + status = SCPE_NOFNC; + } + } else + status = eth_write(xq->var->etherface, &xq->var->write_buffer, NULL); + + xq->var->stats.xmit += 1; + if (status != SCPE_OK) { /* not implemented or unattached */ + sim_debug(DBG_WRN, xq->dev, "Packet Write Error!\n"); + xq->var->stats.fail += 1; + xq->var->xring[i].tmd0 = XQ_TMD0_ERR1; + xq->var->xring[i].tmd1 = 100 + xq->var->write_buffer.len * 8; /* arbitrary value */ + xq->var->xring[i].tmd1 |= XQ_TMD1_LCA; + } else { + if (DBG_PCK & xq->dev->dctrl) + eth_packet_trace_ex(xq->var->etherface, xq->var->write_buffer.msg, xq->var->write_buffer.len, "xq-write", DBG_DAT & xq->dev->dctrl, DBG_PCK); + xq->var->xring[i].tmd0 = 0; + xq->var->xring[i].tmd1 = 100 + xq->var->write_buffer.len * 8; /* arbitrary value */ + } + sim_debug(DBG_WRN, xq->dev, "XBDL completed processing write\n"); + /* clear transmit buffer */ + xq->var->write_buffer.len = 0; + xq->var->xring[i].tmd2 = XQ_TMD2_RON | XQ_TMD2_TON; + } + + Map_ReadW (tdra+(uint32)(((char *)(&xq->var->xring[xq->var->tbindx].tmd3))-((char *)&xq->var->xring)), sizeof(xq->var->xring[xq->var->tbindx].tmd3), (uint16 *)&xq->var->xring[xq->var->tbindx].tmd3); + if (xq->var->xring[xq->var->tbindx].tmd3 & XQ_TMD3_OWN) + xq->var->xring[i].tmd2 |= XQ_TMD2_EOR; + + /* Update transmit descriptor in memory (only done after we've processed the contents) */ + /* Note: We're updating all but the end of the descriptor (which we never change) */ + /* AND the driver will be allowed to change once the changed tmd3 (ownership) */ + /* is noted so we avoid walking on its changes */ + xq->var->xring[i].tmd3 |= XQ_TMD3_OWN; /* Return Descriptor to Driver */ + status = Map_WriteW (tdra+i*sizeof(xq->var->xring[i]), sizeof(xq->var->xring[i])-8, (uint16 *)&xq->var->xring[i]); + if (status != SCPE_OK) + return xq_nxm_error(xq); + + } while (0 == (xq->var->xring[xq->var->tbindx].tmd3 & XQ_TMD3_OWN)); + + if (descriptors_consumed) { + + /* Interrupt for Packet Transmission Completion */ + xq_setint(xq); + + if (xq->var->coalesce_latency == 0) + xq_svc(&xq->unit[0]); /* service any received data */ + } else { + /* There appears to be a bug in the VMS SCS/XQ driver when it uses chained + buffers to transmit a packet. It updates the transmit buffer ring in the + correct order (i.e. clearing the ownership on the last packet segment + first), but it writes a transmit request to the ARQR register after adjusting + the ownership of EACH buffer piece. This results in us being awakened once + and finding nothing to do. We ignore this and the next write the ARQR will + properly cause the packet transmission. + */ + sim_debug(DBG_WRN, xq->dev, "xq_process_turbo_xbdl() - Nothing to Transmit\n"); + } + + return status; +} + t_stat xq_process_loopback(CTLR* xq, ETH_PACK* pack) { - ETH_PACK reply; - ETH_MAC physical_address; + ETH_PACK response; + ETH_MAC *physical_address; t_stat status; - int offset = pack->msg[14] | (pack->msg[15] << 8); + int offset = 16 + (pack->msg[14] | (pack->msg[15] << 8)); int function = pack->msg[offset] | (pack->msg[offset+1] << 8); sim_debug(DBG_TRC, xq->dev, "xq_process_loopback()\n"); @@ -1243,19 +1669,27 @@ t_stat xq_process_loopback(CTLR* xq, ETH_PACK* pack) if (function != 2 /*forward*/) return SCPE_NOFNC; - /* create reply packet */ - memcpy (&reply, pack, sizeof(ETH_PACK)); - memcpy (physical_address, xq->var->setup.valid ? xq->var->setup.macs[0] : xq->var->mac, sizeof(ETH_MAC)); - memcpy (&reply.msg[0], &reply.msg[offset+2], sizeof(ETH_MAC)); - memcpy (&reply.msg[6], physical_address, sizeof(ETH_MAC)); - memcpy (&reply.msg[offset+2], physical_address, sizeof(ETH_MAC)); - reply.msg[offset] = 0x01; - offset += 8; - reply.msg[14] = offset & 0xFF; - reply.msg[15] = (offset >> 8) & 0xFF; + /* create forward response packet */ + memcpy (&response, pack, sizeof(ETH_PACK)); + if (xq->var->mode == XQ_T_DELQA_PLUS) + physical_address = &xq->var->init.phys; + else + if (xq->var->setup.valid) + physical_address = &xq->var->setup.macs[0]; + else + physical_address = &xq->var->mac; + memcpy (&response.msg[0], &response.msg[offset+2], sizeof(ETH_MAC)); + memcpy (&response.msg[6], physical_address, sizeof(ETH_MAC)); + offset += 8 - 16; /* Account for the Ethernet Header and Offset value in this number */ + response.msg[14] = offset & 0xFF; + response.msg[15] = (offset >> 8) & 0xFF; - /* send reply packet */ - status = eth_write(xq->var->etherface, &reply, NULL); + /* send response packet */ + status = eth_write(xq->var->etherface, &response, NULL); + ++xq->var->stats.loop; + + if (DBG_PCK & xq->dev->dctrl) + eth_packet_trace_ex(xq->var->etherface, response.msg, response.len, ((function == 1) ? "xq-loopbackreply" : "xq-loopbackforward"), DBG_DAT & xq->dev->dctrl, DBG_PCK); return status; } @@ -1327,7 +1761,11 @@ t_stat xq_process_local (CTLR* xq, ETH_PACK* pack) void xq_read_callback(CTLR* xq, int status) { xq->var->stats.recv += 1; - if (xq->var->csr & XQ_CSR_RE) { /* receiver enabled */ + + if (DBG_PCK & xq->dev->dctrl) + eth_packet_trace_ex(xq->var->etherface, xq->var->read_buffer.msg, xq->var->read_buffer.len, "xq-recvd", DBG_DAT & xq->dev->dctrl, DBG_PCK); + + if ((xq->var->csr & XQ_CSR_RE) || (xq->var->mode == XQ_T_DELQA_PLUS)) { /* receiver enabled */ /* process any packets locally that can be */ t_stat status = xq_process_local (xq, &xq->var->read_buffer); @@ -1336,6 +1774,7 @@ void xq_read_callback(CTLR* xq, int status) if (status != SCPE_OK) ethq_insert(&xq->var->ReadQ, 2, &xq->var->read_buffer, status); } else { + xq->var->stats.dropped += 1; sim_debug(DBG_WRN, xq->dev, "packet received with receiver disabled\n"); } } @@ -1356,6 +1795,13 @@ void xq_sw_reset(CTLR* xq) int i; sim_debug(DBG_TRC, xq->dev, "xq_sw_reset()\n"); + ++xq->var->stats.reset; + + /* Return DELQA-T to DELQA Normal mode */ + if (xq->var->type == XQ_T_DELQA_PLUS) { + xq->var->mode = XQ_T_DELQA; + xq->var->iba = xq->var->srr = 0; + } /* reset csr bits */ xq_csr_set_clr(xq, set_bits, (uint16) ~set_bits); @@ -1384,6 +1830,10 @@ void xq_sw_reset(CTLR* xq) memcpy (filters[count++], xq->var->setup.macs[i], sizeof(ETH_MAC)); eth_filter (xq->var->etherface, count, filters, xq->var->setup.multicast, xq->var->setup.promiscuous); } + + /* Stop receive polling until the receiver is reenabled */ + xq_stop_receiver(xq); + } /* write registers: */ @@ -1398,11 +1848,25 @@ t_stat xq_wr_var(CTLR* xq, int32 data) xq->var->var = (data & XQ_VEC_IV); break; case XQ_T_DELQA: + case XQ_T_DELQA_PLUS: xq->var->var = (xq->var->var & XQ_VEC_RO) | (data & XQ_VEC_RW); /* if switching to DEQNA-LOCK mode clear VAR<14:10> */ - if (~xq->var->var & XQ_VEC_MS) + if (~xq->var->var & XQ_VEC_MS) { + xq->var->mode = XQ_T_DEQNA; xq->var->var &= ~(XQ_VEC_OS | XQ_VEC_RS | XQ_VEC_ST); + } else { + xq->var->mode = XQ_T_DELQA; + } + + /* if Self Test is on, turn it off to signal completion */ + if (xq->var->var & XQ_VEC_RS) { + xq->var->var &= ~XQ_VEC_RS; + if (!xq->var->etherface) + xq->var->var |= XQ_VEC_S1; /* Indicate No Network Connection */ + else + xq->var->var &= ~XQ_VEC_ST; /* Set success Status */ + } break; } @@ -1619,15 +2083,21 @@ t_stat xq_wr_csr(CTLR* xq, int32 data) return SCPE_OK; } -#if 0 /* controller should ALWAYS have an active timer if enabled (for HW sanity) */ - /* start/stop receive timer when RE transitions */ - if ((xq->var->csr ^ data) & XQ_CSR_RE) { - if (data & XQ_CSR_RE) - sim_activate(&xq->unit[0], clock_cosched (tmxr_poll)); - else - sim_cancel(&xq->unit[0]); + /* start receiver when RE transitions to set */ + if (~xq->var->csr & XQ_CSR_RE & data) { + sim_debug(DBG_REG, xq->dev, "xq_wr_csr(data=0x%08X) - receiver started\n", data); + + /* start the read service timer or enable asynch reading as appropriate */ + xq_start_receiver(xq); + } + + /* stop receiver when RE transitions to clear */ + if (xq->var->csr & XQ_CSR_RE & ~data) { + sim_debug(DBG_REG, xq->dev, "xq_wr_csr(data=0x%08X) - receiver stopped\n", data); + + /* stop the read service timer or disable asynch reading as appropriate */ + xq_stop_receiver(xq); } -#endif /* update CSR bits */ xq_csr_set_clr (xq, set_bits, clr_bits); @@ -1641,37 +2111,194 @@ t_stat xq_wr_csr(CTLR* xq, int32 data) return SCPE_OK; } +void xq_start_receiver(CTLR* xq) +{ + if (!xq->var->etherface) + return; + + /* start the read service timer or enable asynch reading as appropriate */ + if (xq->var->must_poll) + sim_activate(xq->unit, (sim_idle_enab ? tmxr_poll : (tmr_poll*clk_tps)/xq->var->poll)); + else + if ((xq->var->poll == 0) || (xq->var->mode == XQ_T_DELQA_PLUS)) + eth_set_async(xq->var->etherface, xq->var->coalesce_latency_ticks); + else + sim_activate(xq->unit, (sim_idle_enab ? tmxr_poll : (tmr_poll*clk_tps)/xq->var->poll)); +} + +void xq_stop_receiver(CTLR* xq) +{ + sim_cancel(&xq->unit[0]); /* Stop Receiving */ + if (xq->var->etherface) + eth_clr_async(xq->var->etherface); +} + +t_stat xq_wr_srqr(CTLR* xq, int32 data) +{ + uint16 set_bits = data & XQ_SRQR_RW; /* set RW set bits */ + + sim_debug(DBG_REG, xq->dev, "xq_wr_srqr(data=0x%08X)\n", data); + + xq->var->srr = set_bits; + + switch (set_bits) { + case XQ_SRQR_STRT: { + t_stat status; + + xq->var->stats.setup += 1; + /* Get init block from memory */ + status = Map_ReadW (xq->var->iba, sizeof(xq->var->init), (uint16 *)&xq->var->init); + if (SCPE_OK != status) { + xq_nxm_error (xq); + } else { + uint32 saved_debug = xq->dev->dctrl; + + /* temporarily turn on Ethernet debugging if setup debugging is enabled */ + if (xq->dev->dctrl & DBG_SET) + xq->dev->dctrl |= DBG_ETH; + + xq_debug_turbo_setup(xq); + + xq->dib->vec = xq->var->init.vector + VEC_Q; + xq->var->tbindx = xq->var->rbindx = 0; + if ((xq->var->sanity.enabled) && (xq->var->init.options & XQ_IN_OP_HIT)) { + xq->var->sanity.quarter_secs = 4*xq->var->init.hit_timeout; + } + xq->var->icr = xq->var->init.options & XQ_IN_OP_INT; + status = eth_filter_hash (xq->var->etherface, 1, &xq->var->init.phys, 0, xq->var->init.mode & XQ_IN_MO_PRO, &xq->var->init.hash_filter); + + xq->dev->dctrl = saved_debug; /* restore original debugging */ + } + /* start the read service timer or enable asynch reading as appropriate */ + xq_start_receiver(xq); + break; + } + case XQ_SRQR_STOP: + xq_stop_receiver(xq); + break; + default: + break; + } + + /* All Writes to SRQR reset the Host Inactivity Timer */ + xq_reset_santmr(xq); + + /* Interrupt after this synchronous request completion */ + xq_setint(xq); + + return SCPE_OK; +} + +t_stat xq_wr_arqr(CTLR* xq, int32 data) +{ + sim_debug(DBG_REG, xq->dev, "xq_wr_arqr(data=0x%08X)\n", data); + + /* initiate transmit activity when requested */ + if (XQ_ARQR_TRQ & data) { + xq_process_turbo_xbdl (xq); + } + /* initiate transmit activity when requested */ + if (XQ_ARQR_RRQ & data) { + xq_process_turbo_rbdl (xq); + } + + /* reset controller when requested */ + if (XQ_ARQR_SR & data) { + xq_sw_reset(xq); + } + + /* All Writes to ARQR reset the Host Inactivity Timer */ + xq_reset_santmr(xq); + + return SCPE_OK; +} + +t_stat xq_wr_icr(CTLR* xq, int32 data) +{ + uint16 old_icr = xq->var->icr; + + sim_debug(DBG_REG, xq->dev, "xq_wr_icr(data=0x%08X)\n", data); + + xq->var->icr = data & XQ_ICR_ENA; + + if (xq->var->icr && !old_icr && xq->var->pending_interrupt) + xq_setint(xq); + + return SCPE_OK; +} + t_stat xq_wr(int32 data, int32 PA, int32 access) { t_stat status; CTLR* xq = xq_pa2ctlr(PA); int index = (PA >> 1) & 07; /* word index */ - sim_debug(DBG_REG, xq->dev, "xq_wr(data=0x%08X, PA=0x%08X[%s], access=%d)\n", data, PA, xq_xmit_regnames[index], access); + sim_debug(DBG_REG, xq->dev, "xq_wr(data=0x%08X, PA=0x%08X[%s], access=%d)\n", data, PA, ((xq->var->mode == XQ_T_DELQA_PLUS) ? xqt_xmit_regnames[index] : xq_xmit_regnames[index]), access); - switch (index) { - case 0: /* these should not be written */ - case 1: + switch (xq->var->mode) { + case XQ_T_DELQA_PLUS: + switch (index) { + case 0: /* IBAL */ + xq->var->iba = (xq->var->iba & 0xFFFF0000) | (data & 0xFFFF); + break; + case 1: /* IBAH */ + xq->var->iba = (xq->var->iba & 0xFFFF) | ((data & 0xFFFF) << 16); + break; + case 2: /* ICR */ + status = xq_wr_icr(xq, data); + break; + case 3: + break; + case 4: /* SRQR */ + status = xq_wr_srqr(xq, data); + break; + case 5: + break; + case 6: + break; + case 7: /* ARQR */ + status = xq_wr_arqr(xq, data); + break; + } break; - case 2: /* receive bdl low bits */ - xq->var->rbdl[0] = data; - break; - case 3: /* receive bdl high bits */ - xq->var->rbdl[1] = data; - status = xq_dispatch_rbdl(xq); /* start receive operation */ - break; - case 4: /* transmit bdl low bits */ - xq->var->xbdl[0] = data; - break; - case 5: /* transmit bdl high bits */ - xq->var->xbdl[1] = data; - status = xq_dispatch_xbdl(xq); /* start transmit operation */ - break; - case 6: /* vector address register */ - status = xq_wr_var(xq, data); - break; - case 7: /* control and status register */ - status = xq_wr_csr(xq, data); + default: /* DEQNA, DELQA Normal */ + switch (index) { + case 0: /* IBAL/XCR0 */ /* these should only be written on a DELQA-T */ + if (xq->var->type == XQ_T_DELQA_PLUS) + xq->var->iba = (xq->var->iba & 0xFFFF0000) | (data & 0xFFFF); + break; + case 1: /* IBAH/XCR1 */ + if (xq->var->type == XQ_T_DELQA_PLUS) { + if (((xq->var->iba & 0xFFFF) == 0x0BAF) && (data == 0xFF00)) { + xq->var->mode = XQ_T_DELQA_PLUS; + xq->var->srr = XQ_SRR_TRBO; + sim_cancel(xq->unit); /* Turn off receive processing until explicitly enabled */ + eth_clr_async(xq->var->etherface); + } + xq->var->iba = (xq->var->iba & 0xFFFF) | ((data & 0xFFFF) << 16); + } + break; + case 2: /* receive bdl low bits */ + xq->var->rbdl[0] = data; + break; + case 3: /* receive bdl high bits */ + xq->var->rbdl[1] = data; + status = xq_dispatch_rbdl(xq); /* start receive operation */ + break; + case 4: /* transmit bdl low bits */ + xq->var->xbdl[0] = data; + break; + case 5: /* transmit bdl high bits */ + xq->var->xbdl[1] = data; + status = xq_dispatch_xbdl(xq); /* start transmit operation */ + break; + case 6: /* vector address register */ + status = xq_wr_var(xq, data); + break; + case 7: /* control and status register */ + status = xq_wr_csr(xq, data); + break; + } break; } return SCPE_OK; @@ -1694,9 +2321,12 @@ t_stat xq_reset(DEVICE* dptr) switch (xq->var->type) { case XQ_T_DEQNA: xq->var->var = 0; + xq->var->mode = XQ_T_DEQNA; break; case XQ_T_DELQA: + case XQ_T_DELQA_PLUS: xq->var->var = XQ_VEC_MS | XQ_VEC_OS; + xq->var->mode = XQ_T_DELQA; break; } xq->dib->vec = 0; @@ -1717,18 +2347,25 @@ t_stat xq_reset(DEVICE* dptr) /* reset ethernet interface */ if (xq->var->etherface) { + /* restore filter on ROM mac address */ status = eth_filter (xq->var->etherface, 1, &xq->var->mac, 0, 0); xq_csr_set_clr(xq, XQ_CSR_OK, 0); /* start service timer */ - sim_activate_abs(&xq->unit[0], tmxr_poll); + sim_activate_abs(&xq->unit[1], (tmr_poll * clk_tps) / 4); + + /* stop the receiver */ + eth_clr_async(xq->var->etherface); } + /* stop the receiver */ + sim_cancel(xq->unit); + /* set hardware sanity controls */ if (xq->var->sanity.enabled) { xq->var->sanity.quarter_secs = XQ_HW_SANITY_SECS * 4/*qsec*/; - xq->var->sanity.max = XQ_HW_SANITY_SECS * xq->var->poll; } + return SCPE_OK; } @@ -1739,7 +2376,7 @@ void xq_reset_santmr(CTLR* xq) sim_debug(DBG_SAN, xq->dev, "SANITY TIMER RESETTING, qsecs: %d\n", xq->var->sanity.quarter_secs); /* reset sanity countdown timer to max count */ - xq->var->sanity.timer = xq->var->sanity.max; + xq->var->sanity.timer = xq->var->sanity.quarter_secs; } } @@ -1750,7 +2387,7 @@ t_stat xq_boot_host(CTLR* xq) The manual says the hardware should force the Qbus BDCOK low for 3.6 microseconds, which will cause the host to reboot. - Since the SIMH Qbus emulator does not have this functionality, we call + Since the SIMH Qbus emulator does not have this functionality, we return a special STOP_ code, and let the CPU stop dispatch routine decide what the appropriate cpu-specific behavior should be. */ @@ -1765,7 +2402,16 @@ t_stat xq_system_id (CTLR* xq, const ETH_MAC dest, uint16 receipt_id) t_stat status; sim_debug(DBG_TRC, xq->dev, "xq_system_id()\n"); - if (xq->var->type != XQ_T_DELQA) /* DELQA-only function */ + + /* reset system ID counter for next event */ + xq->var->idtmr = XQ_SYSTEM_ID_SECS * 4; + + if (xq->var->coalesce_latency) { + /* Adjust latency ticks based on calibrated timer values */ + xq->var->coalesce_latency_ticks = (tmr_poll * clk_tps * xq->var->coalesce_latency) / 1000000; + } + + if (xq->var->type == XQ_T_DEQNA) /* DELQA-only function */ return SCPE_NOFNC; memset (&system_id, 0, sizeof(system_id)); @@ -1811,11 +2457,16 @@ t_stat xq_system_id (CTLR* xq, const ETH_MAC dest, uint16 receipt_id) msg[41] = 0x00; /* type */ msg[42] = 0x01; /* length */ msg[43] = 0x11; /* value (0x11=DELQA) */ + if (xq->var->type == XQ_T_DELQA_PLUS) /* DELQA-T has different Device ID */ + msg[43] = 0x4B; /* value (0x4B(75)=DELQA-T) */ /* write system id */ system_id.len = 60; status = eth_write(xq->var->etherface, &system_id, NULL); + if (DBG_PCK & xq->dev->dctrl) + eth_packet_trace_ex(xq->var->etherface, system_id.msg, system_id.len, "xq-systemid", DBG_DAT & xq->dev->dctrl, DBG_PCK); + return status; } @@ -1827,44 +2478,59 @@ t_stat xq_svc(UNIT* uptr) CTLR* xq = xq_unit2ctlr(uptr); /* if the receiver is enabled */ - if (xq->var->csr & XQ_CSR_RE) { + if ((xq->var->mode == XQ_T_DELQA_PLUS) || (xq->var->csr & XQ_CSR_RE)) { t_stat status; - int queue_size; /* First pump any queued packets into the system */ - if ((xq->var->ReadQ.count > 0) && (~xq->var->csr & XQ_CSR_RL)) - status = xq_process_rbdl(xq); + if ((xq->var->ReadQ.count > 0) && ((xq->var->mode == XQ_T_DELQA_PLUS) || (~xq->var->csr & XQ_CSR_RL))) + xq_process_rbdl(xq); /* Now read and queue packets that have arrived */ - /* This is repeated as long as they are available and we have room */ - do - { - queue_size = xq->var->ReadQ.count; + /* This is repeated as long as they are available */ + do { /* read a packet from the ethernet - processing is via the callback */ status = eth_read (xq->var->etherface, &xq->var->read_buffer, xq->var->rcallback); - } while (queue_size != xq->var->ReadQ.count); + } while (status); /* Now pump any still queued packets into the system */ - if ((xq->var->ReadQ.count > 0) && (~xq->var->csr & XQ_CSR_RL)) - status = xq_process_rbdl(xq); + if ((xq->var->ReadQ.count > 0) && ((xq->var->mode == XQ_T_DELQA_PLUS) || (~xq->var->csr & XQ_CSR_RL))) + xq_process_rbdl(xq); } + /* resubmit service timer */ + if ((xq->var->must_poll) || (xq->var->poll && (xq->var->mode != XQ_T_DELQA_PLUS))) + sim_activate(uptr, (sim_idle_enab ? tmxr_poll : (tmr_poll*clk_tps)/xq->var->poll)); + + return SCPE_OK; +} + +/* +** service routine - used for timer based activities +*/ +t_stat xq_tmrsvc(UNIT* uptr) +{ + CTLR* xq = xq_unit2ctlr(uptr); + /* has sanity timer expired? if so, reboot */ if (xq->var->sanity.enabled) if (--xq->var->sanity.timer <= 0) - xq_boot_host(xq); + if (xq->var->mode != XQ_T_DELQA_PLUS) + return xq_boot_host(xq); + else { /* DELQA-T Host Inactivity Timer expiration means switch out of DELQA-T mode */ + sim_debug(DBG_TRC, xq->dev, "xq_tmrsvc(DELQA-PLUS Host Inactivity Expired\n"); + xq->var->mode = XQ_T_DELQA; + xq->var->iba = xq->var->srr = 0; + xq->var->var = XQ_VEC_MS | XQ_VEC_OS; + } /* has system id timer expired? if so, do system id */ if (--xq->var->idtmr <= 0) { const ETH_MAC mop_multicast = {0xAB, 0x00, 0x00, 0x02, 0x00, 0x00}; xq_system_id(xq, mop_multicast, 0); - - /* reset system ID counter for next event */ - xq->var->idtmr = XQ_SYSTEM_ID_SECS * xq->var->poll; } /* resubmit service timer */ - sim_activate(&xq->unit[0], tmxr_poll); + sim_activate(uptr, (tmr_poll * clk_tps) / 4); return SCPE_OK; } @@ -1904,6 +2570,27 @@ t_stat xq_attach(UNIT* uptr, char* cptr) xq->var->etherface = 0; return status; } + if (xq->var->poll == 0) { + status = eth_set_async(xq->var->etherface, xq->var->coalesce_latency_ticks); + if (status != SCPE_OK) { + free(tptr); + free(xq->var->etherface); + xq->var->etherface = 0; + return status; + } + xq->var->must_poll = 0; + } else { + xq->var->must_poll = (SCPE_OK != eth_clr_async(xq->var->etherface)); + } + if (SCPE_OK != eth_check_address_conflict (xq->var->etherface, &xq->var->mac)) { + char buf[32]; + + eth_mac_fmt(&xq->var->mac, buf); /* format ethernet mac address */ + printf("%s: MAC Address Conflict on LAN for address %s, change the MAC address to a unique value\n", xq->dev->name, buf); + if (sim_log) fprintf (sim_log, "%s: MAC Address Conflict on LAN for address %s, change the MAC address to a unique value\n", xq->dev->name, buf); + eth_close(xq->var->etherface); + return SCPE_NOATT; + } uptr->filename = tptr; uptr->flags |= UNIT_ATT; @@ -1930,8 +2617,9 @@ t_stat xq_detach(UNIT* uptr) free(uptr->filename); uptr->filename = NULL; uptr->flags &= ~UNIT_ATT; - /* cancel service timer */ + /* cancel service timers */ sim_cancel(&xq->unit[0]); + sim_cancel(&xq->unit[1]); } /* turn off transceiver power indicator */ @@ -1942,6 +2630,16 @@ t_stat xq_detach(UNIT* uptr) void xq_setint(CTLR* xq) { + if (xq->var->mode == XQ_T_DELQA_PLUS) { + if (!xq->var->icr) { + xq->var->pending_interrupt = 1; + return; + } + xq->var->pending_interrupt = 0; + } + + sim_debug(DBG_TRC, xq->dev, "xq_setint() - Generate Interrupt\n"); + xq->var->irq = 1; SET_INT(XQ); return; @@ -2030,12 +2728,16 @@ void xq_debug_setup(CTLR* xq) { int i; char buffer[20]; + + if (!(sim_deb && (xq->dev->dctrl & DBG_SET))) + return; + if (xq->var->write_buffer.msg[0]) - printf ("%s: setup> MOP info present!\n", xq->dev->name); + sim_debug(DBG_SET, xq->dev, "%s: setup> MOP info present!\n", xq->dev->name); for (i = 0; i < XQ_FILTER_MAX; i++) { eth_mac_fmt(&xq->var->setup.macs[i], buffer); - printf ("%s: setup> set addr[%d]: %s\n", xq->dev->name, i, buffer); + sim_debug(DBG_SET, xq->dev, "%s: setup> set addr[%d]: %s\n", xq->dev->name, i, buffer); } if (xq->var->write_buffer.len > 128) { @@ -2045,7 +2747,55 @@ void xq_debug_setup(CTLR* xq) if (len & XQ_SETUP_PM) strcat(buffer, "PM "); if (len & XQ_SETUP_LD) strcat(buffer, "LD "); if (len & XQ_SETUP_ST) strcat(buffer, "ST "); - printf ("%s: setup> Length [%d =0x%X, LD:%d, ST:%d] info: %s\n", + sim_debug(DBG_SET, xq->dev, "%s: setup> Length [%d =0x%X, LD:%d, ST:%d] info: %s\n", xq->dev->name, len, len, (len & XQ_SETUP_LD) >> 2, (len & XQ_SETUP_ST) >> 4, buffer); } } + +void xq_debug_turbo_setup(CTLR* xq) +{ + int i; + char buffer[64] = ""; + + if (!(sim_deb && (xq->dev->dctrl & DBG_SET))) + return; + + sim_debug(DBG_SET, xq->dev, "%s: setup> Turbo Initialization Block!\n", xq->dev->name); + + if (xq->var->init.mode & XQ_IN_MO_PRO) strcat(buffer, "PRO "); + if (xq->var->init.mode & XQ_IN_MO_INT) strcat(buffer, "INT "); + if (xq->var->init.mode & XQ_IN_MO_DRT) strcat(buffer, "DRC "); + if (xq->var->init.mode & XQ_IN_MO_DTC) strcat(buffer, "DTC "); + if (xq->var->init.mode & XQ_IN_MO_LOP) strcat(buffer, "LOP "); + sim_debug(DBG_SET, xq->dev, "%s: setup> set Mode: %s\n", xq->dev->name, buffer); + + eth_mac_fmt(&xq->var->init.phys, buffer); + sim_debug(DBG_SET, xq->dev, "%s: setup> set Physical MAC Address: %s\n", xq->dev->name, buffer); + + buffer[0] = '\0'; + for (i = 0; i < sizeof(xq->var->init.hash_filter); i++) + sprintf(&buffer[strlen(buffer)], "%02X ", xq->var->init.hash_filter[i]); + sim_debug(DBG_SET, xq->dev, "%s: setup> set Multicast Hash: %s\n", xq->dev->name, buffer); + + buffer[0] = '\0'; + if (xq->var->init.options & XQ_IN_OP_HIT) strcat(buffer, "HIT "); + if (xq->var->init.options & XQ_IN_OP_INT) strcat(buffer, "INT "); + sim_debug(DBG_SET, xq->dev, "%s: setup> set Options: %s\n", xq->dev->name, buffer); + + sim_debug(DBG_SET, xq->dev, "%s: setup> set Vector: %d =0x%X\n", + xq->dev->name, xq->var->init.vector, xq->var->init.vector); + + sim_debug(DBG_SET, xq->dev, "%s: setup> set Host Inactivity Timeout: %d seconds\n", + xq->dev->name, xq->var->init.hit_timeout); + + buffer[0] = '\0'; + for (i = 0; i < sizeof(xq->var->init.bootpassword); i++) + sprintf(&buffer[strlen(buffer)], "%02X ", xq->var->init.bootpassword[i]); + + sim_debug(DBG_SET, xq->dev, "%s: setup> set Boot Password: %s\n", xq->dev->name, buffer); + + sim_debug(DBG_SET, xq->dev, "%s: setup> set Receive Ring Buffer Address: %02X%04X\n", + xq->dev->name, xq->var->init.rdra_h, xq->var->init.rdra_l); + sim_debug(DBG_SET, xq->dev, "%s: setup> set Transmit Ring Buffer Address: %02X%04X\n", + xq->dev->name, xq->var->init.tdra_h, xq->var->init.tdra_l); +} diff --git a/PDP11/pdp11_xq.h b/PDP11/pdp11_xq.h index 737552c7..f3b9210a 100644 --- a/PDP11/pdp11_xq.h +++ b/PDP11/pdp11_xq.h @@ -28,6 +28,14 @@ Modification history: + 03-Mar-08 MP Added DELQA-T (aka DELQA Plus) device emulation support. + 06-Feb-08 MP Added dropped frame statistics to record when the receiver discards + received packets due to the receiver being disabled, or due to the + XQ device's packet receive queue being full. Also removed the + filter statistic counter since there was no code which ever set it. + 29-Jan-08 MP Dynamically determine the timer polling rate based on the + calibrated tmr_poll and clk_tps values of the simulator. + 23-Jan-08 MP Added debugging support to display packet headers and packet data 07-Jul-05 RMS Removed extraneous externs 20-Jan-04 DTH Added new sanity timer and system id timer 19-Jan-04 DTH Added XQ_SERVICE_INTERVAL, poll @@ -84,12 +92,11 @@ extern int32 int_req[IPL_HLVL]; #define XQ_HW_SANITY_SECS 240 /* seconds before HW sanity timer expires */ #define XQ_MAX_CONTROLLERS 2 /* maximum controllers allowed */ -enum xq_type {XQ_T_DEQNA, XQ_T_DELQA}; +enum xq_type {XQ_T_DEQNA, XQ_T_DELQA, XQ_T_DELQA_PLUS}; struct xq_sanity { int enabled; /* sanity timer enabled? 2=HW, 1=SW, 0=off */ int quarter_secs; /* sanity timer value in 1/4 seconds */ - int max; /* maximum timeout (based on poll) */ int timer; /* countdown timer */ }; @@ -104,17 +111,132 @@ struct xq_setup { ETH_MAC macs[XQ_FILTER_MAX]; /* MAC addresses to respond to */ }; +struct xq_turbo_init_block { /* DELQA-T Initialization Block */ + uint16 mode; +#define XQ_IN_MO_PRO 0x8000 /* Promiscuous Mode */ +#define XQ_IN_MO_INT 0x0040 /* Internal Loopback Mode */ +#define XQ_IN_MO_DRT 0x0020 /* Disable Retry */ +#define XQ_IN_MO_DTC 0x0008 /* Disable Transmit CRC */ +#define XQ_IN_MO_LOP 0x0004 /* Loopback */ + ETH_MAC phys; /* Physical MAC Address */ + ETH_MULTIHASH hash_filter; /* 64bit LANCE Hash Filter for Multicast Address selection */ + uint16 rdra_l; + uint16 rdra_h; + uint16 tdra_l; + uint16 tdra_h; + uint16 options; +#define XQ_IN_OP_HIT 0x0002 /* Host Inactivity Timer Enable Flag */ +#define XQ_IN_OP_INT 0x0001 /* Interrupt Enable Flag*/ + uint16 vector; /* Interrupt Vector */ + uint16 hit_timeout; /* Host Inactivity Timer Timeout Value */ + uint8 bootpassword[6]; /* MOP Console Boot Password */ +}; + +/* DELQA-T Mode - Transmit Buffer Descriptor */ +struct transmit_buffer_descriptor { + uint16 tmd0; +#define XQ_TMD0_ERR1 0x4000 /* Error Summary. The OR of TMD1 (LC0, LCA, and RTR) */ +#define XQ_TMD0_MOR 0x1000 /* More than one retry on transmit */ +#define XQ_TMD0_ONE 0x0800 /* One retry on transmit */ +#define XQ_TMD0_DEF 0x0400 /* Deferral during transmit */ + uint16 tmd1; +#define XQ_TMD1_LCO 0x1000 /* Late collision on transmit - packet not transmitted */ +#define XQ_TMD1_LCA 0x0800 /* Loss of carrier on transmit - packet not transmitted */ +#define XQ_TMD1_RTR 0x0400 /* Retry error on transmit - packet not transmitted */ +#define XQ_TMD1_TDR 0x03FF /* Time Domain Reflectometry value */ + uint16 tmd2; +#define XQ_TMD2_ERR2 0x8000 /* Error Summary. The OR of TMD2 (BBL, CER, and MIS) */ +#define XQ_TMD2_BBL 0x4000 /* Babble error on transmit */ +#define XQ_TMD2_CER 0x2000 /* Collision error on transmit */ +#define XQ_TMD2_MIS 0x1000 /* Packet lost on receive */ +#define XQ_TMD2_EOR 0x0800 /* End Of Receive Ring Reached */ +#define XQ_TMD2_RON 0x0020 /* Receiver On */ +#define XQ_TMD2_TON 0x0010 /* Transmitter On */ + uint16 tmd3; +#define XQ_TMD3_OWN 0x8000 /* Ownership field. 0 = DELQA-T, 1 = Host Driver */ +#define XQ_TMD3_FOT 0x4000 /* First Of Two flag. 1 = first in chained, 0 = no chain or last in chain */ +#define XQ_TMD3_BCT 0x0FFF /* Byte Count */ + uint16 ladr; /* Low 16bits of Buffer Address */ + uint16 hadr; /* Most significant bits of the Buffer Address */ + uint16 hostuse1; + uint16 hostuse2; +}; +#define XQ_TURBO_XM_BCNT 12 /* Transmit Buffer Descriptor Count */ + +struct receive_buffer_descriptor { + uint16 rmd0; +#define XQ_RMD0_ERR3 0x4000 /* Error Summary. The OR of FRA, CRC, OFL and BUF */ +#define XQ_RMD0_FRA 0x2000 /* Framing error on receive */ +#define XQ_RMD0_OFL 0x1000 /* Overflow error on receive (Giant packet) */ +#define XQ_RMD0_CRC 0x0800 /* CRC error on receive */ +#define XQ_RMD0_BUF 0x0400 /* Internal device buffer error. Part of Giant packet lost */ +#define XQ_RMD0_STP 0x0200 /* Start of Packet Flag */ +#define XQ_RMD0_ENP 0x0100 /* End of Packet Flag */ + uint16 rmd1; +#define XQ_RMD1_MCNT 0x0FFF /* Message byte count (including CRC) */ + uint16 rmd2; +#define XQ_RMD2_ERR4 0x8000 /* Error Summary. The OR of RMD2 (RBL, CER, and MIS) */ +#define XQ_RMD2_BBL 0x4000 /* Babble error on transmit */ +#define XQ_RMD2_CER 0x2000 /* Collision error on transmit */ +#define XQ_RMD2_MIS 0x1000 /* Packet lost on receive */ +#define XQ_RMD2_EOR 0x0800 /* End Of Receive Ring Reached */ +#define XQ_RMD2_RON 0x0020 /* Receiver On */ +#define XQ_RMD2_TON 0x0010 /* Transmitter On */ + uint16 rmd3; +#define XQ_RMD3_OWN 0x8000 /* Ownership field. 0 = DELQA-T, 1 = Host Driver */ + uint16 ladr; /* Low 16bits of Buffer Address */ + uint16 hadr; /* Most significant bits of the Buffer Address */ + uint16 hostuse1; + uint16 hostuse2; +}; +#define XQ_TURBO_RC_BCNT 32 /* Receive Buffer Descriptor Count */ + struct xq_stats { int recv; /* received packets */ - int filter; /* filtered packets */ + int dropped; /* received packets dropped */ int xmit; /* transmitted packets */ int fail; /* transmit failed */ int runt; /* runts */ + int reset; /* reset count */ int giant; /* oversize packets */ int setup; /* setup packets */ int loop; /* loopback packets */ }; +#pragma pack(2) +struct xq_mop_counters { + uint16 seconds; /* Seconds since last zeroed */ + uint32 b_rcvd; /* Bytes Received */ + uint32 b_xmit; /* Bytes Transmitted */ + uint32 p_rcvd; /* Packets Received */ + uint32 p_xmit; /* Packets Transmitted */ + uint32 mb_rcvd; /* Multicast Bytes Received */ + uint32 mp_rcvd; /* Multicast Packets Received */ + uint32 p_x_col1; /* Packets Transmitted Initially Deferred */ + uint32 p_x_col2; /* Packets Transmitted after 2 attempts */ + uint32 p_x_col3; /* Packets Transmitted after 3+ attempts */ + uint16 p_x_fail; /* Transmit Packets Aborted (Send Failure) */ + uint16 p_x_f_bitmap; /* Transmit Packets Aborted (Send Failure) Bitmap */ +#define XQ_XF_RTRY 0x0001 /* Excessive Collisions */ +#define XQ_XF_LCAR 0x0002 /* Loss of Carrier */ +#define XQ_XF_MLEN 0x0010 /* Data Block Too Long */ +#define XQ_XF_LCOL 0x0020 /* Late Collision */ + uint16 p_r_fail; /* Packets received with Error (Receive Failure) */ + uint16 p_r_f_bitmap; /* Packets received with Error (Receive Failure) Bitmap */ +#define XQ_RF_CRC 0x0001 /* Block Check Error */ +#define XQ_RF_FRAM 0x0002 /* Framing Error */ +#define XQ_RF_MLEN 0x0004 /* Message Length Error */ + uint16 h_dest_err; /* Host Counter - Unrecognized Frame Destination Error */ + uint16 r_p_lost_i; /* Receive Packet Lost: Internal Buffer Error */ + uint16 r_p_lost_s; /* Receive Packet Lost: System Buffer Error (Unavailable or Truncated) */ + uint16 h_no_buf; /* Host Counter - User Buffer Unavailable */ + uint32 mb_xmit; /* Multicast Bytes Tramsmitted */ + uint16 reserved1; /* */ + uint16 reserved2; /* */ + uint16 babble; /* Babble Counter */ +}; +#pragma pack() + struct xq_meb { /* MEB block */ uint8 type; uint8 add_lo; @@ -128,18 +250,36 @@ struct xq_device { /*+ initialized values - DO NOT MOVE */ ETH_PCALLBACK rcallback; /* read callback routine */ ETH_PCALLBACK wcallback; /* write callback routine */ - ETH_MAC mac; /* MAC address */ + ETH_MAC mac; /* Hardware MAC address */ enum xq_type type; /* controller type */ - int poll; /* poll ethernet times/sec */ + enum xq_type mode; /* controller operating mode */ + uint16 poll; /* configured poll ethernet times/sec for receive */ + uint16 coalesce_latency; /* microseconds to hold-off interrupts when not polling */ + uint16 coalesce_latency_ticks; /* instructions in coalesce_latency microseconds */ struct xq_sanity sanity; /* sanity timer information */ /*- initialized values - DO NOT MOVE */ /* I/O register storage */ - uint16 addr[6]; + uint16 rbdl[2]; uint16 xbdl[2]; uint16 var; uint16 csr; + + uint16 srr; /* Status and Response Register - DELQA-T only */ + uint16 srqr; /* Synchronous Request Register - DELQA-T only */ + uint32 iba; /* Init Block Address Register - DELQA-T only */ + uint16 icr; /* Interrupt Request Register - DELQA-T only */ + uint16 pending_interrupt; /* Pending Interrupt - DELQA-T only */ + struct xq_turbo_init_block + init; + struct transmit_buffer_descriptor + xring[XQ_TURBO_XM_BCNT]; /* Transmit Buffer Ring */ + uint32 tbindx; /* Transmit Buffer Ring Index */ + struct receive_buffer_descriptor + rring[XQ_TURBO_RC_BCNT]; /* Receive Buffer Ring */ + uint32 rbindx; /* Receive Buffer Ring Index */ + uint32 irq; /* interrupt request flag */ /* buffers, etc. */ @@ -151,11 +291,11 @@ struct xq_device { uint32 rbdl_ba; uint32 xbdl_ba; ETH_DEV* etherface; - int receiving; ETH_PACK read_buffer; ETH_PACK write_buffer; ETH_QUE ReadQ; - int idtmr; /* countdown for ID Timer */ + uint32 idtmr; /* countdown for ID Timer */ + uint32 must_poll; /* receiver must poll instead of counting on asynch polls */ }; struct xq_controller { @@ -207,6 +347,7 @@ typedef struct xq_controller CTLR; #define XQ_VEC_RO 0x5C02 /* Read-Only bits */ #define XQ_VEC_RW 0xA3FD /* Read/Write bits */ +/* DEQNA - DELQA Normal Mode Buffer Descriptors */ #define XQ_DSC_V 0x8000 /* Valid bit */ #define XQ_DSC_C 0x4000 /* Chain bit */ #define XQ_DSC_E 0x2000 /* End of Message bit [Transmit only] */ @@ -214,11 +355,38 @@ typedef struct xq_controller CTLR; #define XQ_DSC_L 0x0080 /* Low Byte Termination bit [Transmit only] */ #define XQ_DSC_H 0x0040 /* High Byte Start bit [Transmit only] */ +/* DEQNA - DELQA Normal Mode Setup Packet Flags */ #define XQ_SETUP_MC 0x0001 /* multicast bit */ #define XQ_SETUP_PM 0x0002 /* promiscuous bit */ #define XQ_SETUP_LD 0x000C /* led bits */ #define XQ_SETUP_ST 0x0070 /* sanity timer bits */ +/* DELQA-T Mode - Status and Response Register (SRR) */ +#define XQ_SRR_FES 0x8000 /* Fatal Error Summary [RO] */ +#define XQ_SRR_CHN 0x4000 /* Chaining Error [RO] */ +#define XQ_SRR_NXM 0x1000 /* Non-Existant Memory Error [RO] */ +#define XQ_SRR_PAR 0x0800 /* Parity Error (Qbus) [RO] */ +#define XQ_SRR_IME 0x0400 /* Internal Memory Error [RO] */ +#define XQ_SRR_TBL 0x0200 /* Transmit Buffer Too Long Error [RO] */ +#define XQ_SRR_RESP 0x0003 /* Synchronous Response Field [RO] */ +#define XQ_SRR_TRBO 0x0001 /* Select Turbo Response [RO] */ +#define XQ_SRR_STRT 0x0002 /* Start Device Response [RO] */ +#define XQ_SRR_STOP 0x0003 /* Stop Device Response [RO] */ + +/* DELQA-T Mode - Synchronous Request Register (SRQR) */ +#define XQ_SRQR_STRT 0x0002 /* Start Device Request [WO] */ +#define XQ_SRQR_STOP 0x0003 /* Stop Device Request [WO] */ +#define XQ_SRQR_RW 0x0003 /* Writable Bits in SRQR [WO] */ + +/* DELQA-T Mode - Asynchronous Request Register (ARQR) */ +#define XQ_ARQR_TRQ 0x8000 /* Transmit Request [WO] */ +#define XQ_ARQR_RRQ 0x0080 /* Receieve Request [WO] */ +#define XQ_ARQR_SR 0x0002 /* Software Reset Request [WO] */ + +/* DELQA-T Mode - Interrupt Control Register (ICR) */ +#define XQ_ICR_ENA 0x0001 /* Interrupt Enabled [WO] */ + + /* debugging bitmaps */ #define DBG_TRC 0x0001 /* trace routine calls */ #define DBG_REG 0x0002 /* trace read/write registers */ @@ -227,7 +395,8 @@ typedef struct xq_controller CTLR; #define DBG_WRN 0x0010 /* display warnings */ #define DBG_SAN 0x0020 /* display sanity timer info */ #define DBG_SET 0x0040 /* display setup info */ -#define DBG_PCK 0x0080 /* display packets */ +#define DBG_PCK 0x0080 /* display packet headers */ +#define DBG_DAT 0x0100 /* display packet data */ #define DBG_ETH 0x8000 /* debug ethernet device */ #endif /* _PDP11_XQ_H */ diff --git a/PDP11/pdp11_xu.c b/PDP11/pdp11_xu.c index 502a6768..c8ff52f4 100644 --- a/PDP11/pdp11_xu.c +++ b/PDP11/pdp11_xu.c @@ -1,7 +1,7 @@ /* pdp11_xu.c: DEUNA/DELUA ethernet controller simulator ------------------------------------------------------------------------------ - Copyright (c) 2003-2007, David T. Hittner + Copyright (c) 2003-2011, David T. Hittner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -30,7 +30,7 @@ Digital DELUA Users Guide, Part# EK-DELUA-UG-002 Digital DEUNA Users Guide, Part# EK-DEUNA-UG-001 These manuals can be found online at: - http://www.spies.com/~aek/pdf/dec/unibus + http://www.bitsavers.org/pdf/dec/unibus Testing performed: 1) Receives/Transmits single packet under custom RSX driver @@ -41,7 +41,13 @@ DECNET - SET HOST in/out, COPY in/out TCP/IP - PING in/out; SET HOST/TELNET in/out, COPY/FTP in/out Clustering - Successfully clustered with AlphaVMS 8.2 - 4) Runs VAX EVDWA diagnostic tests 1-10; tests 11-19 (M68000/ROM/RAM) fail + 4) VMS 4.7 on VAX780 summary: + (Jan/2011: Win7 x64 host; MS VC++ 2008; SIMH v3.8-2 rc1 base; WinPcap 4.1) + LAT - SET HOST/LAT in (no outbound exists) + DECNET - SET HOST in/out, DIR in/out, COPY in/out + TCP/IP - no kit available to test + Clustering - not tested + 5) Runs VAX EVDWA diagnostic tests 1-10; tests 11-19 (M68000/ROM/RAM) fail Known issues: 1) Most auxiliary commands are not implemented yet. @@ -56,6 +62,14 @@ Modification history: + 12-Jan-11 DTH Added SHOW XU FILTERS modifier + 11-Jan-11 DTH Corrected SELFTEST command, enabling use by VMS 3.7, VMS 4.7, and Ultrix 1.1 + 09-Dec-10 MP Added address conflict check during attach. + 06-Dec-10 MP Added loopback processing support + 30-Nov-10 MP Fixed the fact that no broadcast packets were received by the DEUNA + 15-Aug-08 MP Fixed transmitted packets to have the correct source MAC address. + Fixed incorrect address filter setting calling eth_filter(). + 23-Jan-08 MP Added debugging support to display packet headers and packet data 18-Jun-07 RMS Added UNIT_IDLE flag 03-May-07 DTH Added missing FC_RMAL command; cleared multicast on write 29-Oct-06 RMS Synced poll and clock @@ -112,6 +126,7 @@ void xu_clrint (CTLR* xu); void xu_process_receive(CTLR* xu); void xu_dump_rxring(CTLR* xu); void xu_dump_txring(CTLR* xu); +t_stat xu_show_filters (FILE* st, UNIT* uptr, int32 val, void* desc); DIB xua_dib = { IOBA_XU, IOLN_XU, &xu_rd, &xu_wr, 1, IVCL (XU), VEC_XU, {&xu_int} }; @@ -143,6 +158,8 @@ MTAB xu_mod[] = { NULL, ð_show, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATS", "STATS", &xu_set_stats, &xu_show_stats, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "FILTERS", "FILTERS", + NULL, &xu_show_filters, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE={DEUNA|DELUA}", &xu_set_type, &xu_show_type, NULL }, { 0 }, @@ -156,6 +173,7 @@ DEBTAB xu_debug[] = { {"WARN", DBG_WRN}, {"REG", DBG_REG}, {"PACKET", DBG_PCK}, + {"DATA", DBG_DAT}, {"ETH", DBG_ETH}, {0} }; @@ -294,20 +312,39 @@ t_stat xu_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc) t_stat xu_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc) { - char* fmt = " %-24s%d\n"; + char* fmt = " %-26s%d\n"; CTLR* xu = xu_unit2ctlr(uptr); struct xu_stats* stats = &xu->var->stats; fprintf(st, "Ethernet statistics:\n"); - fprintf(st, fmt, "Seconds since cleared:", stats->secs); - fprintf(st, fmt, "Recv frames:", stats->frecv); - fprintf(st, fmt, "Recv dbytes:", stats->rbytes); - fprintf(st, fmt, "Xmit frames:", stats->ftrans); - fprintf(st, fmt, "Xmit dbytes:", stats->tbytes); - fprintf(st, fmt, "Recv frames(multicast):", stats->mfrecv); - fprintf(st, fmt, "Recv dbytes(multicast):", stats->mrbytes); - fprintf(st, fmt, "Xmit frames(multicast):", stats->mftrans); - fprintf(st, fmt, "Xmit dbytes(multicast):", stats->mtbytes); + fprintf(st, fmt, "Seconds since cleared:", stats->secs); + fprintf(st, fmt, "Recv frames:", stats->frecv); + fprintf(st, fmt, "Recv dbytes:", stats->rbytes); + fprintf(st, fmt, "Xmit frames:", stats->ftrans); + fprintf(st, fmt, "Xmit dbytes:", stats->tbytes); + fprintf(st, fmt, "Recv frames(multicast):", stats->mfrecv); + fprintf(st, fmt, "Recv dbytes(multicast):", stats->mrbytes); + fprintf(st, fmt, "Xmit frames(multicast):", stats->mftrans); + fprintf(st, fmt, "Xmit dbytes(multicast):", stats->mtbytes); + fprintf(st, fmt, "Loopback forward Frames:", stats->loopf); + return SCPE_OK; +} + +t_stat xu_show_filters (FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xu = xu_unit2ctlr(uptr); + char buffer[20]; + int i; + + fprintf(st, "Filters:\n"); + for (i=0; ivar->setup.macs[i], buffer); + fprintf(st, " [%2d]: %s\n", i, buffer); + } + if (xu->var->setup.multicast) + fprintf(st, "All Multicast Receive Mode\n"); + if (xu->var->setup.promiscuous) + fprintf(st, "Promiscuous Receive Mode\n"); return SCPE_OK; } @@ -359,13 +396,63 @@ void bit_stat16(uint16* stat, uint16 bits) *stat |= bits; } +t_stat xu_process_loopback(CTLR* xu, ETH_PACK* pack) +{ + ETH_PACK response; + ETH_MAC physical_address; + t_stat status; + int offset = 16 + (pack->msg[14] | (pack->msg[15] << 8)); + int function = pack->msg[offset] | (pack->msg[offset+1] << 8); + + sim_debug(DBG_TRC, xu->dev, "xu_process_loopback()\n"); + + if (function != 2 /*forward*/) + return SCPE_NOFNC; + + /* create forward response packet */ + memcpy (&response, pack, sizeof(ETH_PACK)); + memcpy (physical_address, xu->var->setup.macs[0], sizeof(ETH_MAC)); + memcpy (&response.msg[0], &response.msg[offset+2], sizeof(ETH_MAC)); + memcpy (&response.msg[6], physical_address, sizeof(ETH_MAC)); + offset += 8; + response.msg[14] = offset & 0xFF; + response.msg[15] = (offset >> 8) & 0xFF; + + /* send response packet */ + status = eth_write(xu->var->etherface, &response, NULL); + ++xu->var->stats.loopf; + + if (DBG_PCK & xu->dev->dctrl) + eth_packet_trace_ex(xu->var->etherface, response.msg, response.len, ((function == 1) ? "xu-loopbackreply" : "xu-loopbackforward"), DBG_DAT & xu->dev->dctrl, DBG_PCK); + + return status; +} + t_stat xu_process_local (CTLR* xu, ETH_PACK* pack) { - return SCPE_NOFNC; /* not implemented yet */ + /* returns SCPE_OK if local processing occurred, + otherwise returns SCPE_NOFNC or some other code */ + int protocol; + + sim_debug(DBG_TRC, xu->dev, "xu_process_local()\n"); + + protocol = pack->msg[12] | (pack->msg[13] << 8); + switch (protocol) { + case 0x0090: /* ethernet loopback */ + return xu_process_loopback(xu, pack); + break; + case 0x0260: /* MOP remote console */ + return SCPE_NOFNC; /* not implemented yet */ + break; + } + return SCPE_NOFNC; } void xu_read_callback(CTLR* xu, int status) { + if (DBG_PCK & xu->dev->dctrl) + eth_packet_trace_ex(xu->var->etherface, xu->var->read_buffer.msg, xu->var->read_buffer.len, "xu-recvd", DBG_DAT & xu->dev->dctrl, DBG_PCK); + /* process any packets locally that can be */ status = xu_process_local (xu, &xu->var->read_buffer); @@ -442,6 +529,8 @@ t_stat xu_system_id (CTLR* xu, const ETH_MAC dest, uint16 receipt_id) /* write system id */ system_id.len = 60; status = eth_write(xu->var->etherface, &system_id, NULL); + if (DBG_PCK & xu->dev->dctrl) + eth_packet_trace_ex(xu->var->etherface, system_id.msg, system_id.len, "xu-systemid", DBG_DAT & xu->dev->dctrl, DBG_PCK); return status; } @@ -524,6 +613,7 @@ void xu_setclrint(CTLR* xu, int32 bits) t_stat xu_sw_reset (CTLR* xu) { t_stat status; + int i; sim_debug(DBG_TRC, xu->dev, "xu_sw_reset()\n"); @@ -559,10 +649,12 @@ t_stat xu_sw_reset (CTLR* xu) /* reset ethernet interface */ memcpy (xu->var->setup.macs[0], xu->var->mac, sizeof(ETH_MAC)); - xu->var->setup.mac_count = 1; + for (i=0; i<6; i++) + xu->var->setup.macs[1][i] = 0xff; /* Broadcast Address */ + xu->var->setup.mac_count = 2; if (xu->var->etherface) status = eth_filter (xu->var->etherface, xu->var->setup.mac_count, - &xu->var->mac, xu->var->setup.multicast, + xu->var->setup.macs, xu->var->setup.multicast, xu->var->setup.promiscuous); /* activate device if not disabled */ @@ -670,24 +762,24 @@ int32 xu_command(CTLR* xu) case FC_RMAL: /* read multicast address list */ mtlen = (xu->var->pcb[2] & 0xFF00) >> 8; udbb = xu->var->pcb[1] | ((xu->var->pcb[2] & 03) << 16); - wstatus = Map_WriteB(udbb, mtlen * 3, (uint8*) &xu->var->setup.macs[1]); - break; + wstatus = Map_WriteB(udbb, mtlen * 3, (uint8*) &xu->var->setup.macs[2]); + break; case FC_WMAL: /* write multicast address list */ mtlen = (xu->var->pcb[2] & 0xFF00) >> 8; -sim_debug(DBG_TRC, xu->dev, "FC_WAL: mtlen=%d\n", mtlen); + sim_debug(DBG_TRC, xu->dev, "FC_WAL: mtlen=%d\n", mtlen); if (mtlen > 10) return PCSR0_PCEI; udbb = xu->var->pcb[1] | ((xu->var->pcb[2] & 03) << 16); - /* clear existing multicast list */ - for (i=1; ivar->setup.macs[i][j] = 0; - } - /* get multicast list from host */ - rstatus = Map_ReadB(udbb, mtlen * 6, (uint8*) &xu->var->setup.macs[1]); + /* clear existing multicast list */ + for (i=2; ivar->setup.macs[i][j] = 0; + } + /* get multicast list from host */ + rstatus = Map_ReadB(udbb, mtlen * 6, (uint8*) &xu->var->setup.macs[2]); if (rstatus == 0) { - xu->var->setup.mac_count = mtlen + 1; + xu->var->setup.mac_count = mtlen + 2; status = eth_filter (xu->var->etherface, xu->var->setup.mac_count, xu->var->setup.macs, xu->var->setup.multicast, xu->var->setup.promiscuous); @@ -807,7 +899,7 @@ sim_debug(DBG_TRC, xu->dev, "FC_WAL: mtlen=%d\n", mtlen); case FC_WMODE: /* write mode register */ value = xu->var->mode; xu->var->mode = xu->var->pcb[1]; -sim_debug(DBG_TRC, xu->dev, "FC_WMODE: mode=%04x\n", xu->var->mode); + sim_debug(DBG_TRC, xu->dev, "FC_WMODE: mode=%04x\n", xu->var->mode); /* set promiscuous and multicast flags */ xu->var->setup.promiscuous = (xu->var->mode & MODE_PROM) ? 1 : 0; @@ -816,7 +908,7 @@ sim_debug(DBG_TRC, xu->dev, "FC_WMODE: mode=%04x\n", xu->var->mode); /* if promiscuous or multicast flags changed, change filter */ if ((value ^ xu->var->mode) & (MODE_PROM | MODE_ENAL)) status = eth_filter (xu->var->etherface, xu->var->setup.mac_count, - &xu->var->mac, xu->var->setup.multicast, + xu->var->setup.macs, xu->var->setup.multicast, xu->var->setup.promiscuous); break; @@ -1151,6 +1243,11 @@ void xu_process_transmit(CTLR* xu) runt = 1; } + /* As described in the DEUNA User Guide (Section 4.7), the DEUNA is responsible + for inserting the appropriate source MAC address in the outgoing packet header, + so we do that now. */ + memcpy(xu->var->write_buffer.msg+6, (uint8*)&xu->var->setup.macs[0], sizeof(ETH_MAC)); + /* are we in internal loopback mode ? */ if ((xu->var->mode & MODE_LOOP) && (xu->var->mode & MODE_INTL)) { /* just put packet in receive buffer */ @@ -1160,6 +1257,9 @@ void xu_process_transmit(CTLR* xu) wstatus = eth_write(xu->var->etherface, &xu->var->write_buffer, xu->var->wcallback); if (wstatus) xu->var->pcsr0 |= PCSR0_PCEI; + else + if (DBG_PCK & xu->dev->dctrl) + eth_packet_trace_ex(xu->var->etherface, xu->var->write_buffer.msg, xu->var->write_buffer.len, "xu-write", DBG_DAT & xu->dev->dctrl, DBG_PCK); } /* update transmit status in transmit buffer */ @@ -1263,7 +1363,14 @@ void xu_port_command (CTLR* xu) break; case CMD_SELFTEST: /* SELFTEST */ - xu_sw_reset(xu); + /* + SELFTEST is a <=15-second self diagnostic test, setting various + error flags and the DONE (DNI) flag when complete. For simulation + purposes, signal completion immediately with no errors. This + inexact behavior could be incompatible with any guest machine + diagnostics that are expecting to be able to monitor the + controller's progress through the diagnostic testing. + */ xu->var->pcsr0 |= PCSR0_DNI; break; @@ -1348,7 +1455,7 @@ t_stat xu_rd(int32 *data, int32 PA, int32 access) *data = xu->var->pcsr3; break; } - sim_debug(DBG_TRC, xu->dev, "xu_rd(), PCSR%d, data=%04x\n", reg, *data); + sim_debug(DBG_REG, xu->dev, "xu_rd(), PCSR%d, data=%04x\n", reg, *data); if (PA & 1) sim_debug(DBG_WRN, xu->dev, "xu_rd(), Unexpected Odd address access of PCSR%d\n", reg); return SCPE_OK; @@ -1375,7 +1482,7 @@ t_stat xu_wr(int32 data, int32 PA, int32 access) strcpy(desc, "Unknown"); break; } - sim_debug(DBG_TRC, xu->dev, "xu_wr(), PCSR%d, data=%08x, PA=%08x, access=%d[%s]\n", reg, data, PA, access, desc); + sim_debug(DBG_REG, xu->dev, "xu_wr(), PCSR%d, data=%08x, PA=%08x, access=%d[%s]\n", reg, data, PA, access, desc); switch (reg) { case 00: /* Clear write-one-to-clear interrupt bits */ @@ -1458,6 +1565,15 @@ t_stat xu_attach(UNIT* uptr, char* cptr) xu->var->etherface = 0; return status; } + if (SCPE_OK != eth_check_address_conflict (xu->var->etherface, &xu->var->mac)) { + char buf[32]; + + eth_mac_fmt(&xu->var->mac, buf); /* format ethernet mac address */ + printf("%s: MAC Address Conflict on LAN for address %s\n", xu->dev->name, buf); + if (sim_log) fprintf (sim_log, "%s: MAC Address Conflict on LAN for address %s\n", xu->dev->name, buf); + eth_close(xu->var->etherface); + return SCPE_NOATT; + } uptr->filename = tptr; uptr->flags |= UNIT_ATT; eth_setcrc(xu->var->etherface, 1); /* enable CRC */ diff --git a/PDP11/pdp11_xu.h b/PDP11/pdp11_xu.h index e07b5a53..83a49394 100644 --- a/PDP11/pdp11_xu.h +++ b/PDP11/pdp11_xu.h @@ -1,7 +1,7 @@ /* pdp11_xu.h: DEUNA/DELUA ethernet controller information ------------------------------------------------------------------------------ - Copyright (c) 2003-2008, David T. Hittner + Copyright (c) 2003-2005, David T. Hittner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -28,6 +28,7 @@ Modification history: + 23-Jan-08 MP Added debugging support to display packet headers and packet data 08-Dec-05 DTH Added load_server, increased UDBSIZE for system ID parameters 07-Jul-05 RMS Removed extraneous externs 05-Jan-04 DTH Added network statistics @@ -66,7 +67,7 @@ extern int32 int_req[IPL_HLVL]; #include "sim_ether.h" #define XU_QUE_MAX 500 /* message queue array */ -#define XU_FILTER_MAX 11 /* mac + 10 multicast addrs */ +#define XU_FILTER_MAX 12 /* mac + broadcast + 10 multicast addrs */ #define XU_SERVICE_INTERVAL 100 /* times per second */ #define XU_ID_TIMER_VAL 540 /* 9 min * 60 sec */ #define UDBSIZE 200 /* max size of UDB (in words) */ @@ -107,6 +108,7 @@ struct xu_stats { uint16 txccf; /* transmit collision test failure */ uint16 porterr; /* port driver errors */ uint16 bablcnt; /* babble counter */ + uint32 loopf; /* loopback frames processed */ }; struct xu_device { @@ -300,7 +302,8 @@ typedef struct xu_controller CTLR; #define DBG_TRC 0x0001 /* trace routine calls */ #define DBG_REG 0x0002 /* trace read/write registers */ #define DBG_WRN 0x0004 /* display warnings */ -#define DBG_PCK 0x0080 /* display packets */ +#define DBG_PCK 0x0080 /* display packet headers */ +#define DBG_DAT 0x0100 /* display packet data */ #define DBG_ETH 0x8000 /* debug ethernet device */ #endif /* _PDP11_XU_H */ diff --git a/PDP18B/pdp18b_defs.h b/PDP18B/pdp18b_defs.h index 83551e54..eef82cf6 100644 --- a/PDP18B/pdp18b_defs.h +++ b/PDP18B/pdp18b_defs.h @@ -1,6 +1,6 @@ /* pdp18b_defs.h: 18b PDP simulator definitions - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 22-May-10 RMS Added check for 64b definitions 30-Oct-06 RMS Added infinite loop stop 14-Jan-04 RMS Revised IO device call interface 18-Oct-03 RMS Added DECtape off reel message @@ -53,6 +54,10 @@ #include "sim_defs.h" /* simulator defns */ +#if defined(USE_INT64) || defined(USE_ADDR64) +#error "18b PDP's do not support 64b values!" +#endif + /* Models: only one should be defined model memory CPU options I/O options diff --git a/PDP8/pdp8_clk.c b/PDP8/pdp8_clk.c index 0659c9aa..f19d982d 100644 --- a/PDP8/pdp8_clk.c +++ b/PDP8/pdp8_clk.c @@ -1,6 +1,6 @@ /* pdp8_clk.c: PDP-8 real-time clock simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP8/pdp8_cpu.c b/PDP8/pdp8_cpu.c index 046449f3..c327c41a 100644 --- a/PDP8/pdp8_cpu.c +++ b/PDP8/pdp8_cpu.c @@ -1,6 +1,6 @@ /* pdp8_cpu.c: PDP-8 CPU simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -27,13 +27,13 @@ 28-Apr-07 RMS Removed clock initialization 30-Oct-06 RMS Added idle and infinite loop detection - 30-Sep-06 RMS Fixed SC value after DVI overflow (found by Don North) - 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 30-Sep-06 RMS Fixed SC value after DVI overflow (Don North) + 22-Sep-05 RMS Fixed declarations (Sterling Garwood) 16-Aug-05 RMS Fixed C++ declaration and cast problems 06-Nov-04 RMS Added =n to SHOW HISTORY 31-Dec-03 RMS Fixed bug in set_cpu_hist 13-Oct-03 RMS Added instruction history - Added TSC8-75 support (from Bernhard Baehr) + Added TSC8-75 support (Bernhard Baehr) 12-Mar-03 RMS Added logical name support 04-Oct-02 RMS Revamped device dispatching, added device number support 06-Jan-02 RMS Added device enable/disable routines diff --git a/PDP8/pdp8_ct.c b/PDP8/pdp8_ct.c index 456ac4bf..9f5f1e27 100644 --- a/PDP8/pdp8_ct.c +++ b/PDP8/pdp8_ct.c @@ -1,6 +1,6 @@ /* pdp8_ct.c: PDP-8 cassette tape simulator - Copyright (c) 2006-2008, Robert M Supnik + Copyright (c) 2006-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -27,7 +27,7 @@ 13-Aug-07 RMS Fixed handling of BEOT 06-Aug-07 RMS Foward op at BOT skips initial file gap - 30-May-2007 RMS Fixed typo (from Norm Lastovica) + 30-May-2007 RMS Fixed typo (Norm Lastovica) Magnetic tapes are represented as a series of variable records of the form: diff --git a/PDP8/pdp8_defs.h b/PDP8/pdp8_defs.h index ea951f7b..1cebab76 100644 --- a/PDP8/pdp8_defs.h +++ b/PDP8/pdp8_defs.h @@ -1,6 +1,6 @@ /* pdp8_defs.h: PDP-8 simulator definitions - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 22-May-10 RMS Added check for 64b definitions 21-Aug-07 RMS Added FPP8 support 13-Dec-06 RMS Added TA8E support 30-Oct-06 RMS Added infinite loop stop @@ -46,6 +47,10 @@ #include "sim_defs.h" /* simulator defns */ +#if defined(USE_INT64) || defined(USE_ADDR64) +#error "PDP-8 does not support 64b values!" +#endif + /* Simulator stop codes */ #define STOP_RSRV 1 /* must be 1 */ diff --git a/PDP8/pdp8_df.c b/PDP8/pdp8_df.c index ac6b3f03..a802e6e0 100644 --- a/PDP8/pdp8_df.c +++ b/PDP8/pdp8_df.c @@ -1,6 +1,6 @@ /* pdp8_df.c: DF32 fixed head disk simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,8 +25,8 @@ df DF32 fixed head disk - 15-May-06 RMS Fixed bug in autosize attach (reported by Dave Gesswein) - 07-Jan-06 RMS Fixed unaligned register access bug (found by Doug Carman) + 15-May-06 RMS Fixed bug in autosize attach (Dave Gesswein) + 07-Jan-06 RMS Fixed unaligned register access bug (Doug Carman) 04-Jan-04 RMS Changed sim_fsize calling sequence 26-Oct-03 RMS Cleaned up buffer copy code 26-Jul-03 RMS Fixed bug in set size routine diff --git a/PDP8/pdp8_dt.c b/PDP8/pdp8_dt.c index 5fdfbcaa..8c452eae 100644 --- a/PDP8/pdp8_dt.c +++ b/PDP8/pdp8_dt.c @@ -1,6 +1,6 @@ /* pdp8_dt.c: PDP-8 DECtape simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,7 +26,7 @@ dt TC08/TU56 DECtape 23-Jun-06 RMS Fixed switch conflict in ATTACH - 07-Jan-06 RMS Fixed unaligned register access bug (found by Doug Carman) + 07-Jan-06 RMS Fixed unaligned register access bug (Doug Carman) 16-Aug-05 RMS Fixed C++ declaration and cast problems 25-Jan-04 RMS Revised for device debug support 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR diff --git a/PDP8/pdp8_fpp.c b/PDP8/pdp8_fpp.c index 4fb04c01..6845dbe8 100644 --- a/PDP8/pdp8_fpp.c +++ b/PDP8/pdp8_fpp.c @@ -1,6 +1,6 @@ /* pdp8_fpp.c: PDP-8 floating point processor (FPP8A) - Copyright (c) 2007-2010, Robert M Supnik + Copyright (c) 2007-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP8/pdp8_lp.c b/PDP8/pdp8_lp.c index f2fc6a21..6c111367 100644 --- a/PDP8/pdp8_lp.c +++ b/PDP8/pdp8_lp.c @@ -1,6 +1,6 @@ /* pdp8_lp.c: PDP-8 line printer simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP8/pdp8_mt.c b/PDP8/pdp8_mt.c index cda142ae..914c8619 100644 --- a/PDP8/pdp8_mt.c +++ b/PDP8/pdp8_mt.c @@ -1,6 +1,6 @@ /* pdp8_mt.c: PDP-8 magnetic tape simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP8/pdp8_pt.c b/PDP8/pdp8_pt.c index 9ba51307..f2b1352a 100644 --- a/PDP8/pdp8_pt.c +++ b/PDP8/pdp8_pt.c @@ -1,6 +1,6 @@ /* pdp8_pt.c: PDP-8 paper tape reader/punch simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP8/pdp8_rf.c b/PDP8/pdp8_rf.c index 7117a95c..aef1c76d 100644 --- a/PDP8/pdp8_rf.c +++ b/PDP8/pdp8_rf.c @@ -1,6 +1,6 @@ /* pdp8_rf.c: RF08 fixed head disk simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,8 +25,8 @@ rf RF08 fixed head disk - 15-May-06 RMS Fixed bug in autosize attach (reported by Dave Gesswein) - 07-Jan-06 RMS Fixed unaligned register access bug (found by Doug Carman) + 15-May-06 RMS Fixed bug in autosize attach (Dave Gesswein) + 07-Jan-06 RMS Fixed unaligned register access bug (Doug Carman) 04-Jan-04 RMS Changed sim_fsize calling sequence 26-Oct-03 RMS Cleaned up buffer copy code 26-Jul-03 RMS Fixed bug in set size routine diff --git a/PDP8/pdp8_rk.c b/PDP8/pdp8_rk.c index dfbc6578..10b0e5ad 100644 --- a/PDP8/pdp8_rk.c +++ b/PDP8/pdp8_rk.c @@ -1,6 +1,6 @@ /* pdp8_rk.c: RK8E cartridge disk simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP8/pdp8_rl.c b/PDP8/pdp8_rl.c index 7b658469..22beb05a 100644 --- a/PDP8/pdp8_rl.c +++ b/PDP8/pdp8_rl.c @@ -1,6 +1,6 @@ /* pdp8_rl.c: RL8A cartridge disk simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,7 +25,7 @@ rl RL8A cartridge disk - 25-Oct-05 RMS Fixed IOT 61 decode bug (found by David Gesswein) + 25-Oct-05 RMS Fixed IOT 61 decode bug (David Gesswein) 16-Aug-05 RMS Fixed C++ declaration and cast problems 04-Jan-04 RMS Changed attach routine to use sim_fsize 25-Apr-03 RMS Revised for extended file support diff --git a/PDP8/pdp8_rx.c b/PDP8/pdp8_rx.c index 03249ee2..7a51df0d 100644 --- a/PDP8/pdp8_rx.c +++ b/PDP8/pdp8_rx.c @@ -1,6 +1,6 @@ /* pdp8_rx.c: RX8E/RX01, RX28/RX02 floppy disk simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,9 +25,9 @@ rx RX8E/RX01, RX28/RX02 floppy disk - 15-May-06 RMS Fixed bug in autosize attach (reported by Dave Gesswein) + 15-May-06 RMS Fixed bug in autosize attach (Dave Gesswein) 04-Jan-04 RMS Changed sim_fsize calling sequence - 05-Nov-03 RMS Fixed bug in RX28 read status (found by Charles Dickman) + 05-Nov-03 RMS Fixed bug in RX28 read status (Charles Dickman) 26-Oct-03 RMS Cleaned up buffer copy code, fixed double density write 25-Apr-03 RMS Revised for extended file support 14-Mar-03 RMS Fixed variable size interaction with save/restore diff --git a/PDP8/pdp8_sys.c b/PDP8/pdp8_sys.c index 3b550941..ed7ff85b 100644 --- a/PDP8/pdp8_sys.c +++ b/PDP8/pdp8_sys.c @@ -1,6 +1,6 @@ /* pdp8_sys.c: PDP-8 simulator interface - Copyright (c) 1993-2009, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -24,7 +24,7 @@ in this Software without prior written authorization from Robert M Supnik. 24-Mar-09 RMS Added link to FPP - 24-Jun-08 RMS Fixed bug in new rim loader (found by Don North) + 24-Jun-08 RMS Fixed bug in new rim loader (Don North) 24-May-08 RMS Fixed signed/unsigned declaration inconsistency 03-Sep-07 RMS Added FPP8 support Rewrote rim and binary loaders diff --git a/PDP8/pdp8_td.c b/PDP8/pdp8_td.c index 7797fadd..d02ee571 100644 --- a/PDP8/pdp8_td.c +++ b/PDP8/pdp8_td.c @@ -1,6 +1,6 @@ /* pdp8_td.c: PDP-8 simple DECtape controller (TD8E) simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP8/pdp8_tsc.c b/PDP8/pdp8_tsc.c index 38ca4273..1850e9ad 100644 --- a/PDP8/pdp8_tsc.c +++ b/PDP8/pdp8_tsc.c @@ -1,6 +1,6 @@ /* pdp8_tsc.c: PDP-8 ETOS timesharing option board (TSC8-75) - Copyright (c) 2003-2008, Robert M Supnik + Copyright (c) 2003-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP8/pdp8_tt.c b/PDP8/pdp8_tt.c index 82a5784c..8ae9b168 100644 --- a/PDP8/pdp8_tt.c +++ b/PDP8/pdp8_tt.c @@ -1,6 +1,6 @@ /* pdp8_tt.c: PDP-8 console terminal simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP8/pdp8_ttx.c b/PDP8/pdp8_ttx.c index 0cb067fa..8c0ca6ed 100644 --- a/PDP8/pdp8_ttx.c +++ b/PDP8/pdp8_ttx.c @@ -1,6 +1,6 @@ /* pdp8_ttx.c: PDP-8 additional terminals simulator - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/SDS/sds_defs.h b/SDS/sds_defs.h index b3dc6405..a74ec36f 100644 --- a/SDS/sds_defs.h +++ b/SDS/sds_defs.h @@ -1,6 +1,6 @@ /* sds_defs.h: SDS 940 simulator definitions - Copyright (c) 2001-2008, Robert M. Supnik + Copyright (c) 2001-2010, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 22-May-10 RMS Added check for 64b definitions 25-Apr-03 RMS Revised for extended file support */ @@ -31,6 +32,10 @@ #include "sim_defs.h" /* simulator defns */ +#if defined(USE_INT64) || defined(USE_ADDR64) +#error "SDS 940 does not support 64b values!" +#endif + /* Simulator stop codes */ #define STOP_IONRDY 1 /* I/O dev not ready */ diff --git a/SDS/sds_mt.c b/SDS/sds_mt.c index ea9c83bc..f7590eec 100644 --- a/SDS/sds_mt.c +++ b/SDS/sds_mt.c @@ -227,7 +227,7 @@ switch (fnc) { /* case function */ } else if ((t == 03610) && sim_is_active (uptr) &&/* skip rec? */ ((mt_inst & DEV_OUT) == 0)) - mt_skip = 1; /* set flag */ + mt_skip = 1; /* set flag */ else CRETINS; break; diff --git a/VAX/vax780_defs.h b/VAX/vax780_defs.h index add4651b..dc36d211 100644 --- a/VAX/vax780_defs.h +++ b/VAX/vax780_defs.h @@ -124,21 +124,21 @@ /* 780 microcode patch 37 - only test LR<23:0> for appropriate length */ -#define ML_LR_TEST(r) if ((uint32)((r) & 0xFFFFFF) > 0x200000) RSVD_OPND_FAULT +#define ML_LR_TEST(r) if (((uint32)((r) & 0xFFFFFF)) > 0x200000) RSVD_OPND_FAULT -/* 780 microcode patch 38 - only test PxBR<31>=1 and xBR<1:0> = 0 */ +/* 780 microcode patch 38 - only test PxBR<31>=1, PxBR<30> = 0, and xBR<1:0> = 0 */ -#define ML_PXBR_TEST(r) if ((((r) & 0x80000000) == 0) || \ - ((r) & 0x00000003)) RSVD_OPND_FAULT -#define ML_SBR_TEST(r) if ((r) & 0x00000003) RSVD_OPND_FAULT +#define ML_PXBR_TEST(r) if (((((uint32)(r)) & 0x80000000) == 0) || \ + ((((uint32)(r)) & 0x40000003) != 0)) RSVD_OPND_FAULT +#define ML_SBR_TEST(r) if ((((uint32)(r)) & 0xC0000003) != 0) RSVD_OPND_FAULT /* 780 microcode patch 78 - only test xCBB<1:0> = 0 */ -#define ML_PA_TEST(r) if ((r) & 0x00000003) RSVD_OPND_FAULT +#define ML_PA_TEST(r) if ((((uint32)(r)) & 0x00000003) != 0) RSVD_OPND_FAULT #define LP_AST_TEST(r) if ((r) > AST_MAX) RSVD_OPND_FAULT -#define LP_MBZ84_TEST(r) if ((r) & 0xF8C00000) RSVD_OPND_FAULT -#define LP_MBZ92_TEST(r) if ((r) & 0x7FC00000) RSVD_OPND_FAULT +#define LP_MBZ84_TEST(r) if ((((uint32)(r)) & 0xF8C00000) != 0) RSVD_OPND_FAULT +#define LP_MBZ92_TEST(r) if ((((uint32)(r)) & 0x7FC00000) != 0) RSVD_OPND_FAULT /* Memory */ diff --git a/VAX/vax780_sbi.c b/VAX/vax780_sbi.c index 7debc2d2..df49c22c 100644 --- a/VAX/vax780_sbi.c +++ b/VAX/vax780_sbi.c @@ -1,6 +1,6 @@ /* vax780_sbi.c: VAX 11/780 SBI - Copyright (c) 2004-2008, Robert M Supnik + Copyright (c) 2004-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -27,9 +27,10 @@ sbi bus controller + 21-Mar-2011 RMS Added autoreboot capability (from Mark Pizzalato) 31-May-2008 RMS Fixed machine_check calling sequence (found by Peter Schorn) 03-May-2006 RMS Fixed writes to ACCS - 28-May-08 RMS Inlined physical memory routines + 28-May-2008 RMS Inlined physical memory routines */ #include "vax_defs.h" @@ -100,6 +101,7 @@ uint32 sbi_sc = 0; /* SBI silo comparator * uint32 sbi_mt = 0; /* SBI maintenance */ uint32 sbi_er = 0; /* SBI error status */ uint32 sbi_tmo = 0; /* SBI timeout addr */ +char cpu_boot_cmd[CBUFSIZE] = { 0 }; /* boot command */ static t_stat (*nexusR[NEXUS_NUM])(int32 *dat, int32 ad, int32 md); static t_stat (*nexusW[NEXUS_NUM])(int32 dat, int32 ad, int32 md); @@ -132,6 +134,8 @@ t_stat sbi_reset (DEVICE *dptr); void sbi_set_tmo (int32 pa); void uba_eval_int (void); t_stat vax780_boot (int32 flag, char *ptr); +t_stat vax780_boot_parse (int32 flag, char *ptr); +t_stat cpu_boot (int32 unitno, DEVICE *dptr); extern t_stat vax780_fload (int flag, char *cptr); extern int32 intexc (int32 vec, int32 cc, int32 ipl, int ei); @@ -175,6 +179,7 @@ REG sbi_reg[] = { { HRDATA (SBIMT, sbi_mt, 32) }, { HRDATA (SBIER, sbi_er, 32) }, { HRDATA (SBITMO, sbi_tmo, 32) }, + { BRDATA (BOOTCMD, cpu_boot_cmd, 16, 8, CBUFSIZE), REG_HRO }, { NULL } }; @@ -246,7 +251,7 @@ if ((ipl < IPL_TTINT) && (tti_int || tto_int)) /* console int */ if (ipl >= IPL_SMAX) /* ipl >= sw max? */ return 0; if ((t = SISR & sw_int_mask[ipl]) == 0) - return 0; /* eligible req */ + return 0; /* eligible req */ for (i = IPL_SMAX; i > ipl; i--) { /* check swre int */ if ((t >> i) & 1) /* req != 0? int */ return i; @@ -587,11 +592,18 @@ sbi_er = sbi_er & ~SBIER_TMOW1C; /* clr SBIER etc */ return cc; } -/* Console entry */ +/* Console entry - only reached if CONHALT is set (AUTORESTART is set */ int32 con_halt (int32 code, int32 cc) { -ABORT (STOP_HALT); +if ((cpu_boot_cmd[0] == 0) || /* saved boot cmd? */ + (vax780_boot_parse (0, cpu_boot_cmd) != SCPE_OK) || /* reparse the boot cmd */ + (reset_all (0) != SCPE_OK) || /* reset the world */ + (cpu_boot (0, NULL) != SCPE_OK)) /* set up boot code */ + ABORT (STOP_BOOT); /* any error? */ +printf ("Rebooting...\n"); +if (sim_log) + fprintf (sim_log, "Rebooting...\n"); return cc; } @@ -604,6 +616,19 @@ return cc; t_stat vax780_boot (int32 flag, char *ptr) { +t_stat r; + +r = vax780_boot_parse (flag, ptr); /* parse the boot cmd */ +if (r != SCPE_OK) /* error? */ + return r; +strncpy (cpu_boot_cmd, ptr, CBUFSIZE); /* save for reboot */ +return run_cmd (flag, "CPU"); +} + +/* Parse boot command, set up registers - also used on reset */ + +t_stat vax780_boot_parse (int32 flag, char *ptr) +{ char gbuf[CBUFSIZE]; char *slptr, *regptr; int32 i, r5v, unitno; @@ -649,7 +674,7 @@ for (i = 0; boot_tab[i].name != NULL; i++) { R[3] = unitno; R[4] = 0; R[5] = r5v; - return run_cmd (flag, "CPU"); + return SCPE_OK; } } return SCPE_NOFNC; @@ -662,8 +687,8 @@ t_stat cpu_boot (int32 unitno, DEVICE *dptr) t_stat r; printf ("Loading boot code from vmb.exe\n"); -if (sim_log) fprintf (sim_log, - "Loading boot code from vmb.exe\n"); +if (sim_log) + fprintf (sim_log, "Loading boot code from vmb.exe\n"); r = load_cmd (0, "-O vmb.exe 200"); if (r != SCPE_OK) return r; diff --git a/VAX/vax780_stddev.c b/VAX/vax780_stddev.c index 512ac36c..ae5da61c 100644 --- a/VAX/vax780_stddev.c +++ b/VAX/vax780_stddev.c @@ -1,6 +1,6 @@ /* vax780_stddev.c: VAX 11/780 standard I/O devices - Copyright (c) 1998-2008, Robert M Supnik + Copyright (c) 1998-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -29,6 +29,7 @@ todr TODR clock tmr interval timer + 21-Mar-11 RMS Added reboot capability 17-Aug-08 RMS Resync TODR on any clock reset 18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock 29-Oct-06 RMS Added clock coscheduler function @@ -207,6 +208,8 @@ t_stat fl_wr_txdb (int32 data); t_bool fl_test_xfr (UNIT *uptr, t_bool wr); void fl_protocol_error (void); +extern int32 con_halt (int32 code, int32 cc); + /* TTI data structures tti_dev TTI device descriptor @@ -778,16 +781,20 @@ else { } else if (sel == TXDB_MISC) { /* misc function? */ switch (data & MISC_MASK) { /* case on function */ + case MISC_CLWS: comm_region[COMM_WRMS] = 0; + case MISC_CLCS: comm_region[COMM_CLDS] = 0; break; + case MISC_SWDN: ABORT (STOP_SWDN); break; + case MISC_BOOT: - ABORT (STOP_BOOT); + con_halt (0, 0); /* set up reboot */ break; } } diff --git a/VAX/vax780_uba.c b/VAX/vax780_uba.c index e12a225d..16e2e910 100644 --- a/VAX/vax780_uba.c +++ b/VAX/vax780_uba.c @@ -619,7 +619,7 @@ for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ if (!uba_map_addr (ba + i, &ma)) /* page inv or NXM? */ return (bc - i); pbc = VA_PAGSIZE - VA_GETOFF (ma); /* left in page */ - if (pbc > (bc - i)) /* limit to rem xfr */ + if (pbc > (bc - i)) /* limit to rem xfr */ pbc = bc - i; if (DEBUG_PRI (uba_dev, UBA_DEB_XFR)) fprintf (sim_deb, ">>UBA: 8b read, ma = %X, bc = %X\n", ma, pbc); diff --git a/VAX/vax_cpu.c b/VAX/vax_cpu.c index 933040f3..b0465784 100644 --- a/VAX/vax_cpu.c +++ b/VAX/vax_cpu.c @@ -1,6 +1,6 @@ /* vax_cpu.c: VAX CPU - Copyright (c) 1998-2010, Robert M Supnik + Copyright (c) 1998-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,7 @@ cpu VAX central processor + 23-Mar-11 RMS Revised for new idle design (from Mark Pizzolato) 24-Apr-10 RMS Added OLDVMS idle timer option Fixed bug in SET CPU IDLE 21-May-08 RMS Removed inline support @@ -175,8 +176,9 @@ #define UNIT_CONH (1u << UNIT_V_CONH) #define UNIT_MSIZE (1u << UNIT_V_MSIZE) #define GET_CUR acc = ACC_MASK (PSL_GETCUR (PSL)) -#define VAX_IDLE_DFLT 1000 -#define OLD_IDLE_DFLT 200 +#define VAX_IDLE_VMS 0x1 +#define VAX_IDLE_ULT 0x2 +#define VAX_IDLE_QUAD 0x3 #define OPND_SIZE 16 #define INST_SIZE 52 @@ -261,9 +263,8 @@ int32 cpu_astop = 0; int32 mchk_va, mchk_ref; /* mem ref param */ int32 ibufl, ibufh; /* prefetch buf */ int32 ibcnt, ppc; /* prefetch ctl */ -uint32 cpu_idle_ipl_mask = 0x8; /* idle if on IPL 3 */ -uint32 cpu_idle_type = 1; /* default to VMS */ -int32 cpu_idle_wait = VAX_IDLE_DFLT; /* for these cycles */ +uint32 cpu_idle_mask = VAX_IDLE_VMS; /* idle mask */ +uint32 cpu_idle_type = 1; /* default VMS */ jmp_buf save_env; REG *pcq_r = NULL; /* PC queue reg ptr */ int32 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ @@ -387,8 +388,8 @@ int32 cpu_get_vsw (int32 sw); SIM_INLINE int32 get_istr (int32 lnt, int32 acc); int32 ReadOcta (int32 va, int32 *opnd, int32 j, int32 acc); t_bool cpu_show_opnd (FILE *st, InstHistory *h, int32 line); -int32 cpu_psl_ipl_idle (int32 newpsl); t_stat cpu_idle_svc (UNIT *uptr); +void cpu_idle (void); /* CPU data structures @@ -445,9 +446,8 @@ REG cpu_reg[] = { { FLDATA (CRDERR, crd_err, 0) }, { FLDATA (MEMERR, mem_err, 0) }, { FLDATA (HLTPIN, hlt_pin, 0) }, - { HRDATA (IDLE_IPL, cpu_idle_ipl_mask, 16), REG_HIDDEN }, - { DRDATA (IDLE_TYPE, cpu_idle_type, 4), REG_HRO }, - { DRDATA (IDLE_WAIT, cpu_idle_wait, 16), REG_HIDDEN }, + { HRDATA (IDLE_MASK, cpu_idle_mask, 16), REG_HIDDEN }, + { DRDATA (IDLE_INDX, cpu_idle_type, 4), REG_HRO }, { BRDATA (PCQ, pcq, 16, 32, PCQ_SIZE), REG_RO+REG_CIRC }, { HRDATA (PCQP, pcq_p, 6), REG_HRO }, { HRDATA (BADABO, badabo, 32), REG_HRO }, @@ -1571,6 +1571,16 @@ for ( ;; ) { case TSTL: CC_IIZZ_L (op0); /* set cc's */ + if ((cc == CC_Z) && + ((((PSL & PSL_IS) != 0) && /* on IS? */ + (PSL_GETIPL (PSL) == 0x1) && /* at IPL 1? */ + ((cpu_idle_mask & VAX_IDLE_ULT) != 0))|| /* running Ultrix or friends? */ + ((PSL_GETIPL (PSL) == 0x0) && /* at IPL 0? */ + ((fault_PC & 0x80000000) != 0) && /* in system space? */ + ((PC - fault_PC) == 6) && /* 6 byte instruction? */ + ((fault_PC & 0x7fffffff) < 0x4000) && /* in low system space? */ + ((cpu_idle_mask & VAX_IDLE_QUAD) != 0)))) /* running Quad or friends? */ + cpu_idle(); /* idle loop */ break; /* Single operand instructions with source, read/write - op src.mx @@ -2109,14 +2119,20 @@ for ( ;; ) { case BRB: BRANCHB (brdisp); /* branch */ - if ((PC == fault_PC) && (PSL_GETIPL (PSL) == 0x1F)) - ABORT (STOP_LOOP); + if (PC == fault_PC) { /* to self? */ + if (PSL_GETIPL (PSL) == 0x1F) /* int locked out? */ + ABORT (STOP_LOOP); /* infinite loop */ + cpu_idle (); /* idle loop */ + } break; case BRW: BRANCHW (brdisp); /* branch */ - if ((PC == fault_PC) && (PSL_GETIPL (PSL) == 0x1F)) - ABORT (STOP_LOOP); + if (PC == fault_PC) { /* to self? */ + if (PSL_GETIPL (PSL) == 0x1F) /* int locked out? */ + ABORT (STOP_LOOP); /* infinite loop */ + cpu_idle (); /* idle loop */ + } break; case BSBB: @@ -2348,8 +2364,13 @@ for ( ;; ) { */ case BBS: - if (op_bb_n (opnd, acc)) /* br if bit set */ + if (op_bb_n (opnd, acc)) { /* br if bit set */ BRANCHB (brdisp); + if (((PSL & PSL_IS) != 0) && /* on IS? */ + (PSL_GETIPL (PSL) == 0x3) && /* at IPL 3? */ + ((cpu_idle_mask & VAX_IDLE_VMS) != 0)) /* running VMS? */ + cpu_idle (); /* idle loop */ + } break; case BBC: @@ -3071,24 +3092,15 @@ opnd[j++] = Read (va + 12, L_LONG, acc); return j; } -/* Check new PSL IPL for idle start - Checked only on exception or REI, not on MTPR #IPL, - to allow for local locking within the idle loop */ +/* Schedule idle before the next instruction */ -int32 cpu_psl_ipl_idle (int32 newpsl) +void cpu_idle (void) { -if (((newpsl ^ PSL) & PSL_IPL) != 0) { - sim_cancel (&cpu_unit); - if (sim_idle_enab && ((newpsl & PSL_CUR) == 0)) { - uint32 newipl = PSL_GETIPL (newpsl); - if (cpu_idle_ipl_mask & (1u << newipl)) - sim_activate (&cpu_unit, cpu_idle_wait); - } - } -return newpsl; +sim_activate_abs (&cpu_unit, 0); +return; } -/* Idle timer has expired with no PSL change */ +/* Idle service */ t_stat cpu_idle_svc (UNIT *uptr) { @@ -3108,15 +3120,17 @@ PSL = PSL_IS | PSL_IPL1F; SISR = 0; ASTLVL = 4; mapen = 0; -if (M == NULL) - M = (uint32 *) calloc (((uint32) MEMSIZE) >> 2, sizeof (uint32)); -if (M == NULL) - return SCPE_MEM; -pcq_r = find_reg ("PCQ", NULL, dptr); -if (pcq_r) +FLUSH_ISTR; /* init I-stream */ +if (M == NULL) { /* first time init? */ + sim_brk_types = sim_brk_dflt = SWMASK ('E'); + pcq_r = find_reg ("PCQ", NULL, dptr); + if (pcq_r == NULL) + return SCPE_IERR; pcq_r->qptr = 0; -else return SCPE_IERR; -sim_brk_types = sim_brk_dflt = SWMASK ('E'); + M = (uint32 *) calloc (((uint32) MEMSIZE) >> 2, sizeof (uint32)); + if (M == NULL) + return SCPE_MEM; + } return build_dib_tab (); } @@ -3381,17 +3395,16 @@ return more; struct os_idle { char *name; uint32 mask; - int32 value; }; static struct os_idle os_tab[] = { - { "VMS", 0x8, VAX_IDLE_DFLT }, - { "NETBSD", 0x2, VAX_IDLE_DFLT }, - { "ULTRIX", 0x2, VAX_IDLE_DFLT }, - { "OPENBSD", 0x1, VAX_IDLE_DFLT }, - { "32V", 0x1, VAX_IDLE_DFLT }, - { "OLDVMS", 0xB, OLD_IDLE_DFLT }, - { NULL, 0, 0 } + { "VMS", VAX_IDLE_VMS }, + { "NETBSD", VAX_IDLE_ULT }, + { "ULTRIX", VAX_IDLE_ULT }, + { "OPENBSD", VAX_IDLE_QUAD }, + { "32V", VAX_IDLE_QUAD }, + { "ALL", VAX_IDLE_VMS|VAX_IDLE_ULT|VAX_IDLE_QUAD }, + { NULL, 0 } }; /* Set and show idle */ @@ -3404,8 +3417,7 @@ if (cptr != NULL) { for (i = 0; os_tab[i].name != NULL; i++) { if (strcmp (os_tab[i].name, cptr) == 0) { cpu_idle_type = i + 1; - cpu_idle_ipl_mask = os_tab[i].mask; - cpu_idle_wait = os_tab[i].value; + cpu_idle_mask = os_tab[i].mask; return sim_set_idle (uptr, val, NULL, desc); } } diff --git a/VAX/vax_cpu1.c b/VAX/vax_cpu1.c index 8e1b71ab..1f0243bf 100644 --- a/VAX/vax_cpu1.c +++ b/VAX/vax_cpu1.c @@ -1,6 +1,6 @@ /* vax_cpu1.c: VAX complex instructions - Copyright (c) 1998-2008, Robert M Supnik + Copyright (c) 1998-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 23-Mar-11 RMS Revised idle design (from Mark Pizzolato) 28-May-08 RMS Inlined physical memory routines 29-Apr-07 RMS Separated base register access checks for 11/780 10-May-06 RMS Added access check on system PTE for 11/780 @@ -109,7 +110,6 @@ extern t_bool chk_tb_ent (uint32 va); extern int32 ReadIPR (int32 rg); extern void WriteIPR (int32 rg, int32 val); extern t_bool BadCmPSL (int32 newpsl); -extern int32 cpu_psl_ipl_idle (int32 newpsl); extern jmp_buf save_env; @@ -1137,9 +1137,9 @@ else { } } if (ei > 0) /* if int, new IPL */ - PSL = cpu_psl_ipl_idle (newpsl | (ipl << PSL_V_IPL)); -else PSL = cpu_psl_ipl_idle (newpsl | /* exc, old IPL/1F */ - ((newpc & 1)? PSL_IPL1F: (oldpsl & PSL_IPL)) | (oldcur << PSL_V_PRV)); + PSL = newpsl | (ipl << PSL_V_IPL); +else PSL = newpsl | /* exc, old IPL/1F */ + ((newpc & 1)? PSL_IPL1F: (oldpsl & PSL_IPL)) | (oldcur << PSL_V_PRV); if (DEBUG_PRI (cpu_dev, LOG_CPU_I)) fprintf (sim_deb, ">>IEX: PC=%08x, PSL=%08x, SP=%08x, VEC=%08x, nPSL=%08x, nSP=%08x\n", PC, oldpsl, oldsp, vec, PSL, SP); @@ -1249,7 +1249,7 @@ else STK[oldcur] = SP; if (DEBUG_PRI (cpu_dev, LOG_CPU_R)) fprintf (sim_deb, ">>REI: PC=%08x, PSL=%08x, SP=%08x, nPC=%08x, nPSL=%08x, nSP=%08x\n", PC, PSL, SP - 8, newpc, newpsl, ((newpsl & IS)? IS: STK[newcur])); -PSL = cpu_psl_ipl_idle ((PSL & PSL_TP) | (newpsl & ~CC_MASK)); /* set PSL */ +PSL = (PSL & PSL_TP) | (newpsl & ~CC_MASK); /* set PSL */ if (PSL & PSL_IS) /* set new stack */ SP = IS; else { diff --git a/VAX/vax_sys.c b/VAX/vax_sys.c index a18a7541..b9f2b95c 100644 --- a/VAX/vax_sys.c +++ b/VAX/vax_sys.c @@ -1,6 +1,6 @@ /* vax_sys.c: VAX simulator interface - Copyright (c) 1998-2008, Robert M Supnik + Copyright (c) 1998-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 21-Mar-11 RMS Modified string for STOP_BOOT message 19-Nov-08 RMS Moved bad block routine to I/O library 03-Nov-05 RMS Added 780 stop codes 04-Sep-05 RMS Fixed missing assignment (found by Peter Schorn) @@ -100,7 +101,7 @@ const char *sim_stop_messages[] = { "Infinite loop", "Sanity timer expired", "Software done", - "Reboot requested", + "Reboot request failed", "Unknown error", "Unknown abort code" }; diff --git a/VAX/vax_syscm.c b/VAX/vax_syscm.c index b55816d0..edc17edf 100644 --- a/VAX/vax_syscm.c +++ b/VAX/vax_syscm.c @@ -1,6 +1,6 @@ /* vax_syscm.c: PDP-11 compatibility mode symbolic decode and parse - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,8 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 22-May-10 RMS Fixed t_addr printouts for 64b big-endian systems + (found by Mark Pizzolato) 12-Nov-06 RMS Fixed operand order in EIS instructions (found by W.F.J. Mueller) 27-Sep-05 RMS Fixed warnings compiling with 64b addresses 15-Sep-04 RMS Cloned from pdp11_sys.c @@ -242,13 +244,13 @@ switch (mode) { case 6: if (reg != 7) fprintf (of, "%-X(%s)", nval, rname[reg]); - else fprintf (of, "%-X", (nval + addr + 4) & 0177777); + else fprintf (of, "%-X", (int32)((nval + addr + 4) & 0177777)); break; case 7: if (reg != 7) fprintf (of, "@%-X(%s)", nval, rname[reg]); - else fprintf (of, "@%-X", (nval + addr + 4) & 0177777); + else fprintf (of, "@%-X", (int32)((nval + addr + 4) & 0177777)); break; } /* end case */ @@ -326,7 +328,7 @@ for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ case I_V_BR: /* cond branch */ fprintf (of, "%s ", opcode[i]); brdisp = (l8b + l8b + ((l8b & 0200)? 0177002: 2)) & 0177777; - fprintf (of, "%-X", (addr + brdisp) & 0177777); + fprintf (of, "%-X", (int32)((addr + brdisp) & 0177777)); break; case I_V_8B: /* 8b */ @@ -336,7 +338,7 @@ for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ case I_V_SOB: /* sob */ fprintf (of, "%s %s,", opcode[i], rname[srcr]); brdisp = (dstm * 2) - 2; - fprintf (of, "%-X", (addr - brdisp) & 0177777); + fprintf (of, "%-X", (int32)((addr - brdisp) & 0177777)); break; case I_V_RSOP: /* rsop */ diff --git a/VAX/vax_sysdev.c b/VAX/vax_sysdev.c index 5f575bd9..169456d9 100644 --- a/VAX/vax_sysdev.c +++ b/VAX/vax_sysdev.c @@ -1,6 +1,6 @@ /* vax_sysdev.c: VAX 3900 system-specific logic - Copyright (c) 1998-2008, Robert M Supnik + Copyright (c) 1998-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -32,6 +32,7 @@ cso console storage output sysd system devices (SSC miscellany) + 23-Dec-10 RMS Added power clear call to boot routine (from Mark Pizzolato) 25-Oct-05 RMS Automated CMCTL extended memory 16-Aug-05 RMS Fixed C++ declaration and cast problems 10-Mar-05 RMS Fixed bug in timer schedule routine (from Mark Hittinger) @@ -275,6 +276,7 @@ extern void txcs_wr (int32 dat); extern void txdb_wr (int32 dat); extern void ioreset_wr (int32 dat); extern uint32 sim_os_msec(); +extern void cpu_idle (void); /* ROM data structures @@ -1556,6 +1558,7 @@ if (*rom == 0) { /* no boot? */ if (r != SCPE_OK) return r; } +sysd_powerup (); return SCPE_OK; } diff --git a/makefile b/makefile index 57cde601..2f9f3abd 100644 --- a/makefile +++ b/makefile @@ -9,12 +9,36 @@ ifeq ($(WIN32),) ifneq (,$(findstring darwin,$(OSTYPE))) OS_CCDEFS = -D_GNU_SOURCE else - OS_CCDEFS = -lrt -lm -D_GNU_SOURCE + ifeq (librt,$(shell if test -e /usr/lib/librt.a; then echo librt; fi)) + OS_CCDEFS = -lrt -lm -D_GNU_SOURCE + else + OS_CCDEFS = -lm -D_GNU_SOURCE + endif endif endif - CC = gcc -std=c99 -U__STRICT_ANSI__ -g $(OS_CCDEFS) -I . - ifeq ($(USE_NETWORK),) - else + ifeq (readline,$(shell if test -e /usr/lib/libreadline.a; then echo readline; fi)) + # Use Locally installed and available readline support + ifeq (ncurses,$(shell if test -e /usr/lib/libncurses.a; then echo ncurses; fi)) + READLINE_CCDEFS = -DHAVE_READLINE -lreadline -lncurses + else + READLINE_CCDEFS = -DHAVE_READLINE -lreadline + endif + endif + ifeq (pcap,$(shell if test -e /usr/lib/libpcap.a; then echo pcap; fi)) + # Use Locally installed and available pcap support + NETWORK_CCDEFS = -DUSE_NETWORK -lpcap + endif + ifeq (tuntap,$(shell if test -e /usr/include/linux/if_tun.h; then echo tuntap; fi)) + # Provide support for Tap networking on Linux + NETWORK_TAP_CCDEFS = -DUSE_TAP_NETWORK + endif + ifeq (bsdtuntap,$(shell if test -e /usr/include/net/if_tun.h; then echo bsdtuntap; fi)) + # Provide support for Tap networking + NETWORK_TAP_CCDEFS = -DUSE_TAP_NETWORK -DUSE_BSDTUNTAP + endif + CC = gcc -std=c99 -U__STRICT_ANSI__ -g $(OS_CCDEFS) -I . $(READLINE_CCDEFS) $(NETWORK_CCDEFS) $(NETWORK_TAP_CCDEFS) + ifneq ($(USE_NETWORK),) + # Assume built from tcpdump.org sources with default install target NETWORK_OPT = -DUSE_NETWORK -isystem /usr/local/include /usr/local/lib/libpcap.a endif else @@ -22,9 +46,8 @@ else LDFLAGS = -lm -lwsock32 -lwinmm CC = gcc -std=c99 -U__STRICT_ANSI__ -O2 -I. EXE = .exe - ifeq ($(USE_NETWORK),) - else - NETWORK_OPT = -DUSE_NETWORK -lwpcap -lpacket + ifneq ($(USE_NETWORK),) + NETWORK_OPT = -DUSE_SHARED endif endif @@ -58,7 +81,7 @@ ECLIPSE = ${NOVAD}/eclipse_cpu.c ${NOVAD}/eclipse_tt.c ${NOVAD}/nova_sys.c \ ${NOVAD}/nova_dkp.c ${NOVAD}/nova_dsk.c ${NOVAD}/nova_lp.c \ ${NOVAD}/nova_mta.c ${NOVAD}/nova_plt.c ${NOVAD}/nova_pt.c \ ${NOVAD}/nova_clk.c ${NOVAD}/nova_tt1.c ${NOVAD}/nova_qty.c -ECLIPSE_OPT = -I ${NOVAD} -DECLIPSE -DUSE_INT64 +ECLIPSE_OPT = -I ${NOVAD} -DECLIPSE PDP18BD = PDP18B @@ -135,7 +158,7 @@ PDP8 = ${PDP8D}/pdp8_cpu.c ${PDP8D}/pdp8_clk.c ${PDP8D}/pdp8_df.c \ PDP8_OPT = -I ${PDP8D} -H316D = H316 +H316D = h316 H316 = ${H316D}/h316_stddev.c ${H316D}/h316_lp.c ${H316D}/h316_cpu.c \ ${H316D}/h316_sys.c ${H316D}/h316_mt.c ${H316D}/h316_fhd.c \ ${H316D}/h316_dp.c @@ -251,7 +274,7 @@ SDS = ${SDSD}/sds_cpu.c ${SDSD}/sds_drm.c ${SDSD}/sds_dsk.c ${SDSD}/sds_io.c \ ${SDSD}/sds_stddev.c ${SDSD}/sds_sys.c SDS_OPT = -I ${SDSD} -SWTPD = SWTP +SWTPD = swtp SWTP = ${SWTPD}/swtp_cpu.c ${SWTPD}/swtp_dsk.c ${SWTPD}/swtp_sio.c \ ${SWTPD}/swtp_sys.c SWTP_OPT = -I ${SWTPD} @@ -269,9 +292,10 @@ all : ${ALL} clean : ifeq ($(WIN32),) - ${RM} ${BIN}* + ${RM} -r ${BIN} else if exist BIN\*.exe del /q BIN\*.exe + if exist BIN rmdir BIN endif # diff --git a/scp.c b/scp.c index f64196a2..9c776a4c 100644 --- a/scp.c +++ b/scp.c @@ -1,6 +1,6 @@ /* scp.c: simulator control program - Copyright (c) 1993-2009, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,11 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 13-Jan-11 MP Added "SHOW SHOW" and "SHOW SHOW" commands + 05-Jan-11 RMS Fixed bug in deposit stride for numeric input (John Dundas) + 23-Dec-10 RMS Clarified some help messages (Mark Pizzolato) + 08-Nov-10 RMS Fixed handling of DO with no arguments (Dave Bryan) + 22-May-10 RMS Added *nix READLINE support (Mark Pizzolato) 08-Feb-09 RMS Fixed warnings in help printouts 29-Dec-08 RMS Fixed implementation of MTAB_NC 24-Nov-08 RMS Revised RESTORE unit logic for consistency @@ -30,8 +35,8 @@ 17-Aug-08 RMS Revert RUN/BOOT to standard, rather than powerup, reset 25-Jul-08 JDB DO cmd missing params now default to null string 29-Jun-08 JDB DO cmd sub_args now allows "\\" to specify literal backslash - 31-Mar-08 RMS Fixed bug in local/global register search (found by Mark Pizzolato) - Fixed bug in restore of RO units (from Mark Pizzolato) + 31-Mar-08 RMS Fixed bug in local/global register search (Mark Pizzolato) + Fixed bug in restore of RO units (Mark Pizzolato) 06-Feb-08 RMS Added SET/SHO/NO BR with default argument 18-Jul-07 RMS Modified match_ext for VMS ext;version support 28-Apr-07 RMS Modified sim_instr invocation to call sim_rtcn_init_all @@ -47,52 +52,52 @@ 14-Feb-06 RMS Upgraded save file format to V3.5 18-Jan-06 RMS Added fprint_stopped_gen Added breakpoint spaces - Fixed unaligned register access (found by Doug Carman) - 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + Fixed unaligned register access (Doug Carman) + 22-Sep-05 RMS Fixed declarations (Sterling Garwood) 30-Aug-05 RMS Revised to trim trailing spaces on file names 25-Aug-05 RMS Added variable default device support 23-Aug-05 RMS Added Linux line history support 16-Aug-05 RMS Fixed C++ declaration and cast problems - 01-May-05 RMS Revised syntax for SET DEBUG (from Dave Bryan) + 01-May-05 RMS Revised syntax for SET DEBUG (Dave Bryan) 22-Mar-05 JDB Modified DO command to allow ten-level nesting - 18-Mar-05 RMS Moved DETACH tests into detach_unit (from Dave Bryan) + 18-Mar-05 RMS Moved DETACH tests into detach_unit (Dave Bryan) Revised interface to fprint_sym, fparse_sym - 07-Feb-05 RMS Added ASSERT command (from Dave Bryan) + 07-Feb-05 RMS Added ASSERT command (Dave Bryan) 02-Feb-05 RMS Fixed bug in global register search 26-Dec-04 RMS Qualified SAVE examine, RESTORE deposit with SIM_SW_REST 10-Nov-04 JDB Fixed logging of errors from cmds in "do" file 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy - Renamed unit OFFLINE/ONLINE to DISABLED/ENABLED (from Dave Bryan) - Revised to flush output files after simulation stop (from Dave Bryan) + Renamed unit OFFLINE/ONLINE to DISABLED/ENABLED (Dave Bryan) + Revised to flush output files after simulation stop (Dave Bryan) 15-Oct-04 RMS Fixed HELP to suppress duplicate descriptions - 27-Sep-04 RMS Fixed comma-separation options in set (from David Bryan) + 27-Sep-04 RMS Fixed comma-separation options in set (David Bryan) 09-Sep-04 RMS Added -p option for RESET 13-Aug-04 RMS Qualified RESTORE detach with SIM_SW_REST - 17-Jul-04 RMS Added ECHO command (from Dave Bryan) + 17-Jul-04 RMS Added ECHO command (Dave Bryan) 12-Jul-04 RMS Fixed problem ATTACHing to read only files - (found by John Dundas) + (John Dundas) 28-May-04 RMS Added SET/SHOW CONSOLE 14-Feb-04 RMS Updated SAVE/RESTORE (V3.2) - RMS Added debug print routines (from Dave Hittner) + RMS Added debug print routines (Dave Hittner) RMS Added sim_vm_parse_addr and sim_vm_fprint_addr RMS Added REG_VMAD support RMS Split out libraries RMS Moved logging function to SCP RMS Exposed step counter interface(s) - RMS Fixed double logging of SHOW BREAK (found by Mark Pizzolato) + RMS Fixed double logging of SHOW BREAK (Mark Pizzolato) RMS Fixed implementation of REG_VMIO RMS Added SET/SHOW DEBUG, SET/SHOW DEBUG, SHOW MODIFIERS, SHOW RADIX RMS Changed sim_fsize to take uptr argument 29-Dec-03 RMS Added Telnet console output stall support 01-Nov-03 RMS Cleaned up implicit detach on attach/restore - Fixed bug in command line read while logging (found by Mark Pizzolato) + Fixed bug in command line read while logging (Mark Pizzolato) 01-Sep-03 RMS Fixed end-of-file problem in dep, idep Fixed error on trailing spaces in dep, idep 15-Jul-03 RMS Removed unnecessary test in reset_all 15-Jun-03 RMS Added register flag REG_VMIO 25-Apr-03 RMS Added extended address support (V3.0) - Fixed bug in SAVE (found by Peter Schorn) + Fixed bug in SAVE (Peter Schorn) Added u5, u6 fields Added logical name support 03-Mar-03 RMS Added sim_fsize @@ -100,36 +105,36 @@ 08-Feb-03 RMS Changed sim_os_sleep to void, match_ext to char* Added multiple actions, .ini file support Added multiple switch evaluations per line - 07-Feb-03 RMS Added VMS support for ! (from Mark Pizzolato) + 07-Feb-03 RMS Added VMS support for ! (Mark Pizzolato) 01-Feb-03 RMS Added breakpoint table extension, actions 14-Jan-03 RMS Added missing function prototypes 10-Jan-03 RMS Added attach/restore flag, dynamic memory size support, case sensitive SET options - 22-Dec-02 RMS Added ! (OS command) feature (from Mark Pizzolato) + 22-Dec-02 RMS Added ! (OS command) feature (Mark Pizzolato) 17-Dec-02 RMS Added get_ipaddr 02-Dec-02 RMS Added EValuate command 16-Nov-02 RMS Fixed bug in register name match algorithm - 13-Oct-02 RMS Fixed Borland compiler warnings (found by Hans Pufal) - 05-Oct-02 RMS Fixed bugs in set_logon, ssh_break (found by David Hittner) + 13-Oct-02 RMS Fixed Borland compiler warnings (Hans Pufal) + 05-Oct-02 RMS Fixed bugs in set_logon, ssh_break (David Hittner) Added support for fixed buffer devices Added support for Telnet console, removed VT support Added help - Added VMS file optimizations (from Robert Alan Byer) + Added VMS file optimizations (Robert Alan Byer) Added quiet mode, DO with parameters, GUI interface, - extensible commands (from Brian Knittel) + extensible commands (Brian Knittel) Added device enable/disable commands - 14-Jul-02 RMS Fixed exit bug in do, added -v switch (from Brian Knittel) + 14-Jul-02 RMS Fixed exit bug in do, added -v switch (Brian Knittel) 17-May-02 RMS Fixed bug in fxread/fxwrite error usage (found by Norm Lastovic) 02-May-02 RMS Added VT emulation interface, changed {NO}LOG to SET {NO}LOG 22-Apr-02 RMS Fixed laptop sleep problem in clock calibration, added - magtape record length error (found by Jonathan Engdahl) + magtape record length error (Jonathan Engdahl) 26-Feb-02 RMS Fixed initialization bugs in do_cmd, get_aval - (found by Brian Knittel) + (Brian Knittel) 10-Feb-02 RMS Fixed problem in clock calibration 06-Jan-02 RMS Moved device enable/disable to simulators 30-Dec-01 RMS Generalized timer packaged, added circular arrays - 19-Dec-01 RMS Fixed DO command bug (found by John Dundas) + 19-Dec-01 RMS Fixed DO command bug (John Dundas) 07-Dec-01 RMS Implemented breakpoint package 05-Dec-01 RMS Fixed bug in universal register logic 03-Dec-01 RMS Added read-only units, extended SET/SHOW, universal registers @@ -144,7 +149,7 @@ Added special modifier print 31-Aug-01 RMS Changed int64 to t_int64 for Windoze (V2.7) 18-Jul-01 RMS Minor changes for Macintosh port - 12-Jun-01 RMS Fixed bug in big-endian I/O (found by Dave Conroy) + 12-Jun-01 RMS Fixed bug in big-endian I/O (Dave Conroy) 27-May-01 RMS Added multiple console support 16-May-01 RMS Added logging 15-May-01 RMS Added features from Tim Litt @@ -183,6 +188,11 @@ #include #include +#if defined(HAVE_READLINE) +#include +#include +#endif + #define EX_D 0 /* deposit */ #define EX_E 1 /* examine */ #define EX_I 2 /* interactive */ @@ -279,11 +289,13 @@ t_stat show_config (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_queue (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_mod_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_log_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_dev_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_device (FILE *st, DEVICE *dptr, int32 flag); @@ -322,6 +334,7 @@ void fprint_help (FILE *st); void fprint_stopped (FILE *st, t_stat r); void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr); char *read_line (char *ptr, int32 size, FILE *stream); +char *read_line_p (char *prompt, char *ptr, int32 size, FILE *stream); REG *find_reg_glob (char *ptr, char **optr, DEVICE **gdptr); char *sim_trim_endspc (char *cptr); @@ -393,7 +406,7 @@ static const char *sim_sa64 = "64b addresses"; #else static const char *sim_sa64 = "32b addresses"; #endif -#if defined USE_NETWORK +#if defined (USE_NETWORK) || defined (USE_SHARED) static const char *sim_snet = "Ethernet support"; #else static const char *sim_snet = "no Ethernet"; @@ -539,17 +552,18 @@ static CTAB cmd_table[] = { "set DISABLED disable device\n" "set DEBUG{=arg} set device debug flags\n" "set NODEBUG={arg} clear device debug flags\n" - "set arg{,arg...} set device parameters\n" + "set arg{,arg...} set device parameters (see show modifiers)\n" "set ENABLED enable unit\n" "set DISABLED disable unit\n" - "set arg{,arg...} set unit parameters\n" + "set arg{,arg...} set unit parameters (see show modifiers)\n" }, { "SHOW", &show_cmd, 0, "sh{ow} br{eak} show breakpoints\n" "sh{ow} con{figuration} show configuration\n" "sh{ow} cons{ole} {arg} show console options\n" "sh{ow} dev{ices} show devices\n" - "sh{ow} m{odifiers} show modifiers\n" + "sh{ow} m{odifiers} show modifiers for all devices\n" + "sh{ow} s{how} show SHOW commands for all devices\n" "sh{ow} n{ames} show logical names\n" "sh{ow} q{ueue} show event queue\n" "sh{ow} ti{me} show simulated time\n" @@ -558,7 +572,8 @@ static CTAB cmd_table[] = { "sh{ow} RADIX show device display radix\n" "sh{ow} DEBUG show device debug flags\n" "sh{ow} MODIFIERS show device modifiers\n" - "sh{ow} NAMES show device logical name\n" + "sh{ow} SHOW show device SHOW commands\n" "sh{ow} {arg,...} show device parameters\n" "sh{ow} {arg,...} show unit parameters\n" }, { "DO", &do_cmd, 1, @@ -669,12 +684,13 @@ else if (*argv[0]) { /* sim name arg? */ } while (stat != SCPE_EXIT) { /* in case exit */ - printf ("sim> "); /* prompt */ if (cptr = sim_brk_getact (cbuf, CBUFSIZE)) /* pending action? */ - printf ("%s\n", cptr); /* echo */ - else if (sim_vm_read != NULL) /* sim routine? */ + printf ("sim> %s\n", cptr); /* echo */ + else if (sim_vm_read != NULL) { /* sim routine? */ + printf ("sim> "); /* prompt */ cptr = (*sim_vm_read) (cbuf, CBUFSIZE, stdin); - else cptr = read_line (cbuf, CBUFSIZE, stdin); /* read command line */ + } + else cptr = read_line_p ("sim> ", cbuf, CBUFSIZE, stdin);/* read with prmopt*/ if (cptr == NULL) /* ignore EOF */ continue; if (*cptr == 0) /* ignore blank */ @@ -863,7 +879,7 @@ for (nargs = 0; nargs < 10; ) { /* extract arguments */ } } /* end for */ -if (nargs <= 0) /* need at least 1 */ +if ((nargs <= 0) || (do_arg [0] == NULL)) /* need at least 1 */ return SCPE_2FARG; if ((fpin = fopen (do_arg[0], "r")) == NULL) { /* file failed to open? */ if (flag == 0) /* cmd line file? */ @@ -1304,6 +1320,7 @@ static SHTAB show_glob_tab[] = { { "TIME", &show_time, 0 }, { "MODIFIERS", &show_mod_names, 0 }, { "NAMES", &show_log_names, 0 }, + { "SHOW", &show_show_commands, 0 }, { "VERSION", &show_version, 1 }, { "CONSOLE", &sim_show_console, 0 }, { "BREAK", &show_break, 0 }, @@ -1319,6 +1336,7 @@ static SHTAB show_dev_tab[] = { { "DEBUG", &show_dev_debug, 0 }, { "MODIFIERS", &show_dev_modifiers, 0 }, { "NAMES", &show_dev_logicals, 0 }, + { "SHOW", &show_dev_show_commands, 0 }, { NULL, NULL, 0 } }; @@ -1634,7 +1652,7 @@ return SCPE_OK; t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { -int any, enb; +int32 any, enb; MTAB *mptr; DEBTAB *dep; @@ -1660,11 +1678,12 @@ if (!enb && (dptr->flags & DEV_DISABLE)) { fprintf (st, ", ENABLED, DISABLED"); else fprintf (st, "%s\tENABLED, DISABLED", sim_dname (dptr)); } -if (any) fprintf (st, "\n"); +if (any) + fprintf (st, "\n"); if ((dptr->flags & DEV_DEBUG) && dptr->debflags) { fprintf (st, "%s\tDEBUG=", sim_dname (dptr)); for (dep = dptr->debflags; dep->name != NULL; dep++) - fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ","), dep->name); + fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name); fprintf (st, "\n"); } return SCPE_OK; @@ -1707,6 +1726,40 @@ if (flag && !((mptr->mask & MTAB_XTD) && (mptr->mask & MTAB_NMO))) return SCPE_OK; } +/* Show show commands */ + +t_stat show_show_commands (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) +{ +int32 i; +DEVICE *dptr; + +if (cptr && (*cptr != 0)) /* now eol? */ + return SCPE_2MARG; +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) + show_dev_show_commands (st, dptr, NULL, flag, cptr); +return SCPE_OK; +} + +t_stat show_dev_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +int32 any, enb; +MTAB *mptr; + +any = enb = 0; +if (dptr->modifiers) { + for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) { + if ((!mptr->disp) || (!mptr->pstring)) + continue; + if (any++) + fprintf (st, ", %s", mptr->pstring); + else fprintf (st, "SHOW %s\t%s", sim_dname (dptr), mptr->pstring); + } + } +if (any) + fprintf (st, "\n"); +return SCPE_OK; +} + /* Breakpoint commands */ t_stat brk_cmd (int32 flg, char *cptr) @@ -3265,6 +3318,7 @@ if ((reason = parse_sym (cptr, addr, uptr, sim_eval, sim_switches)) > 0) { sim_eval[0] = get_uint (cptr, rdx, mask, &reason); if (reason != SCPE_OK) return reason; + reason = dfltinc; } count = (1 - reason + (dptr->aincr - 1)) / dptr->aincr; @@ -3356,9 +3410,42 @@ return SCPE_OK; char *read_line (char *cptr, int32 size, FILE *stream) { +return read_line_p (NULL, cptr, size, stream); +} + +/* read_line_p read line with prompt + + Inputs: + prompt = pointer to prompt string + cptr = pointer to buffer + size = maximum size + stream = pointer to input stream + Outputs: + optr = pointer to first non-blank character + NULL if EOF +*/ + +char *read_line_p (char *prompt, char *cptr, int32 size, FILE *stream) +{ char *tptr; +#if defined(HAVE_READLINE) +if (prompt) { /* interactive? */ + char *tmpc = readline (prompt); /* get cmd line */ + if (tmpc == NULL) /* bad result? */ + cptr = NULL; + else { + strncpy (cptr, tmpc, size); /* copy result */ + free (tmpc) ; /* free temp */ + } + } +else cptr = fgets (cptr, size, stream); /* get cmd line */ +#else +if (prompt) /* interactive? */ + printf ("%s", prompt); /* display prompt */ cptr = fgets (cptr, size, stream); /* get cmd line */ +#endif + if (cptr == NULL) { clearerr (stream); /* clear error */ return NULL; /* ignore EOF */ @@ -3372,12 +3459,14 @@ for (tptr = cptr; tptr < (cptr + size); tptr++) { /* remove cr or nl */ } while (isspace (*cptr)) /* trim leading spc */ cptr++; -if (*cptr == ';') *cptr = 0; /* ignore comment */ +if (*cptr == ';') /* ignore comment */ + *cptr = 0; #if defined (HAVE_READLINE) -add_history (cptr); - +if (prompt) + add_history (cptr); #endif + return cptr; } diff --git a/sim_console.c b/sim_console.c index 497c8e6d..0d1d19ee 100644 --- a/sim_console.c +++ b/sim_console.c @@ -1,6 +1,6 @@ /* sim_console.c: simulator console I/O library - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 20-Jan-11 MP Added support for BREAK key on Windows 30-Sep-06 RMS Fixed non-printable characters in KSR mode 22-Jun-06 RMS Implemented SET/SHOW PCHAR 31-May-06 JDB Fixed bug if SET CONSOLE DEBUG with no argument @@ -662,17 +663,42 @@ return SCPE_OK; #elif defined (_WIN32) -#include #include #include #include #define RAW_MODE 0 static HANDLE std_input; +static HANDLE std_output; static DWORD saved_mode; - + +static BOOL WINAPI +ControlHandler(DWORD dwCtrlType) + { + DWORD Mode; + extern void int_handler (int sig); + + switch (dwCtrlType) + { + case CTRL_BREAK_EVENT: // Use CTRL-Break or CTRL-C to simulate + case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode + int_handler(0); + return TRUE; + case CTRL_CLOSE_EVENT: // Window is Closing + case CTRL_LOGOFF_EVENT: // User is logging off + if (!GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &Mode)) + return TRUE; // Not our User, so ignore + case CTRL_SHUTDOWN_EVENT: // System is shutting down + int_handler(0); + return TRUE; + } + return FALSE; + } + t_stat sim_ttinit (void) { +SetConsoleCtrlHandler( ControlHandler, TRUE ); std_input = GetStdHandle (STD_INPUT_HANDLE); +std_output = GetStdHandle (STD_OUTPUT_HANDLE); if ((std_input == INVALID_HANDLE_VALUE) || !GetConsoleMode (std_input, &saved_mode)) return SCPE_TTYERR; @@ -710,24 +736,49 @@ return SCPE_OK; t_stat sim_os_poll_kbd (void) { -int c; +int c = -1; +DWORD nkbevents, nkbevent; +INPUT_RECORD rec; +extern int32 sim_switches; -if (!_kbhit ()) - return SCPE_OK; -c = _getch (); +if (!GetNumberOfConsoleInputEvents(std_input, &nkbevents)) + return SCPE_TTYERR; +while (c == -1) { + if (0 == nkbevents) + return SCPE_OK; + if (!ReadConsoleInput(std_input, &rec, 1, &nkbevent)) + return SCPE_TTYERR; + if (0 == nkbevent) + return SCPE_OK; + --nkbevents; + if (rec.EventType == KEY_EVENT) { + if (rec.Event.KeyEvent.bKeyDown) { + if (0 == rec.Event.KeyEvent.uChar.UnicodeChar) { /* Special Character/Keys? */ + if (rec.Event.KeyEvent.wVirtualKeyCode == VK_PAUSE) /* Pause/Break Key */ + c = sim_brk_char | SCPE_BREAK; + else + if (rec.Event.KeyEvent.wVirtualKeyCode == '2') /* ^@ */ + c = 0; /* return NUL */ + } else + c = rec.Event.KeyEvent.uChar.AsciiChar; + } + } + } if ((c & 0177) == sim_del_char) c = 0177; if ((c & 0177) == sim_int_char) return SCPE_STOP; -if (sim_brk_char && ((c & 0177) == sim_brk_char)) +if ((sim_brk_char && ((c & 0177) == sim_brk_char)) || (c & SCPE_BREAK)) return SCPE_BREAK; return c | SCPE_KFLAG; } t_stat sim_os_putchar (int32 c) { +DWORD unused; + if (c != 0177) - _putch (c); + WriteConsoleA(std_output, &c, 1, &unused, NULL); return SCPE_OK; } diff --git a/sim_console_old.c b/sim_console_old.c new file mode 100644 index 00000000..497c8e6d --- /dev/null +++ b/sim_console_old.c @@ -0,0 +1,1180 @@ +/* sim_console.c: simulator console I/O library + + Copyright (c) 1993-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Sep-06 RMS Fixed non-printable characters in KSR mode + 22-Jun-06 RMS Implemented SET/SHOW PCHAR + 31-May-06 JDB Fixed bug if SET CONSOLE DEBUG with no argument + 22-Nov-05 RMS Added central input/output conversion support + 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy + 28-Oct-04 JDB Fixed SET CONSOLE to allow comma-separated parameters + 20-Aug-04 RMS Added OS/2 EMX fixes (from Holger Veit) + 14-Jul-04 RMS Revised Windows console code (from Dave Bryan) + 28-May-04 RMS Added SET/SHOW CONSOLE + RMS Added break, delete character maps + 02-Jan-04 RMS Removed timer routines, added Telnet console routines + RMS Moved console logging to OS-independent code + 25-Apr-03 RMS Added long seek support from Mark Pizzolato + Added Unix priority control from Mark Pizzolato + 24-Sep-02 RMS Removed VT support, added Telnet console support + Added CGI support (from Brian Knittel) + Added MacOS sleep (from Peter Schorn) + 14-Jul-02 RMS Added Windows priority control from Mark Pizzolato + 20-May-02 RMS Added Windows VT support from Fischer Franz + 01-Feb-02 RMS Added VAX fix from Robert Alan Byer + 19-Sep-01 RMS More Mac changes + 31-Aug-01 RMS Changed int64 to t_int64 for Windoze + 20-Jul-01 RMS Added Macintosh support (from Louis Chretien, Peter Schorn, + and Ben Supnik) + 15-May-01 RMS Added logging support + 05-Mar-01 RMS Added clock calibration support + 08-Dec-00 BKR Added OS/2 support (from Bruce Ray) + 18-Aug-98 RMS Added BeOS support + 13-Oct-97 RMS Added NetBSD terminal support + 25-Jan-97 RMS Added POSIX terminal I/O support + 02-Jan-97 RMS Fixed bug in sim_poll_kbd + + This module implements the following routines to support terminal I/O: + + sim_poll_kbd - poll for keyboard input + sim_putchar - output character to console + sim_putchar_s - output character to console, stall if congested + sim_set_console - set console parameters + sim_show_console - show console parameters + sim_tt_inpcvt - convert input character per mode + sim_tt_outcvt - convert output character per mode + + sim_ttinit - called once to get initial terminal state + sim_ttrun - called to put terminal into run state + sim_ttcmd - called to return terminal to command state + sim_ttclose - called once before the simulator exits + sim_os_poll_kbd - poll for keyboard input + sim_os_putchar - output character to console + + The first group is OS-independent; the second group is OS-dependent. + + The following routines are exposed but deprecated: + + sim_set_telnet - set console to Telnet port + sim_set_notelnet - close console Telnet port + sim_show_telnet - show console status +*/ + +#include "sim_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include + +#define KMAP_WRU 0 +#define KMAP_BRK 1 +#define KMAP_DEL 2 +#define KMAP_MASK 0377 +#define KMAP_NZ 0400 + +int32 sim_int_char = 005; /* interrupt character */ +int32 sim_brk_char = 000; /* break character */ +int32 sim_tt_pchar = 0x00002780; +#if defined (_WIN32) || defined (__OS2__) || (defined (__MWERKS__) && defined (macintosh)) +int32 sim_del_char = '\b'; /* delete character */ +#else +int32 sim_del_char = 0177; +#endif +TMLN sim_con_ldsc = { 0 }; /* console line descr */ +TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc }; /* console line mux */ + +extern volatile int32 stop_cpu; +extern int32 sim_quiet, sim_deb_close; +extern FILE *sim_log, *sim_deb; +extern DEVICE *sim_devices[]; + +/* Set/show data structures */ + +static CTAB set_con_tab[] = { + { "WRU", &sim_set_kmap, KMAP_WRU | KMAP_NZ }, + { "BRK", &sim_set_kmap, KMAP_BRK }, + { "DEL", &sim_set_kmap, KMAP_DEL |KMAP_NZ }, + { "PCHAR", &sim_set_pchar, 0 }, + { "TELNET", &sim_set_telnet, 0 }, + { "NOTELNET", &sim_set_notelnet, 0 }, + { "LOG", &sim_set_logon, 0 }, + { "NOLOG", &sim_set_logoff, 0 }, + { "DEBUG", &sim_set_debon, 0 }, + { "NODEBUG", &sim_set_deboff, 0 }, + { NULL, NULL, 0 } + }; + +static SHTAB show_con_tab[] = { + { "WRU", &sim_show_kmap, KMAP_WRU }, + { "BRK", &sim_show_kmap, KMAP_BRK }, + { "DEL", &sim_show_kmap, KMAP_DEL }, + { "PCHAR", &sim_show_pchar, 0 }, + { "LOG", &sim_show_log, 0 }, + { "TELNET", &sim_show_telnet, 0 }, + { "DEBUG", &sim_show_debug, 0 }, + { NULL, NULL, 0 } + }; + +static int32 *cons_kmap[] = { + &sim_int_char, + &sim_brk_char, + &sim_del_char + }; + +/* Console I/O package. + + The console terminal can be attached to the controlling window + or to a Telnet connection. If attached to a Telnet connection, + the console is described by internal terminal multiplexor + sim_con_tmxr and internal terminal line description sim_con_ldsc. +*/ + +/* SET CONSOLE command */ + +t_stat sim_set_console (int32 flag, char *cptr) +{ +char *cvptr, gbuf[CBUFSIZE]; +CTAB *ctptr; +t_stat r; + +if ((cptr == NULL) || (*cptr == 0)) + return SCPE_2FARG; +while (*cptr != 0) { /* do all mods */ + cptr = get_glyph_nc (cptr, gbuf, ','); /* get modifier */ + if (cvptr = strchr (gbuf, '=')) /* = value? */ + *cvptr++ = 0; + get_glyph (gbuf, gbuf, 0); /* modifier to UC */ + if (ctptr = find_ctab (set_con_tab, gbuf)) { /* match? */ + r = ctptr->action (ctptr->arg, cvptr); /* do the rest */ + if (r != SCPE_OK) + return r; + } + else return SCPE_NOPARAM; + } +return SCPE_OK; +} + +/* SHOW CONSOLE command */ + +t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +SHTAB *shptr; +int32 i; + +if (*cptr == 0) { /* show all */ + for (i = 0; show_con_tab[i].name; i++) + show_con_tab[i].action (st, dptr, uptr, show_con_tab[i].arg, cptr); + return SCPE_OK; + } +while (*cptr != 0) { + cptr = get_glyph (cptr, gbuf, ','); /* get modifier */ + if (shptr = find_shtab (show_con_tab, gbuf)) + shptr->action (st, dptr, uptr, shptr->arg, cptr); + else return SCPE_NOPARAM; + } +return SCPE_OK; +} + +/* Set keyboard map */ + +t_stat sim_set_kmap (int32 flag, char *cptr) +{ +DEVICE *dptr = sim_devices[0]; +int32 val, rdx; +t_stat r; + +if ((cptr == NULL) || (*cptr == 0)) + return SCPE_2FARG; +if (dptr->dradix == 16) rdx = 16; +else rdx = 8; +val = (int32) get_uint (cptr, rdx, 0177, &r); +if ((r != SCPE_OK) || + ((val == 0) && (flag & KMAP_NZ))) + return SCPE_ARG; +*(cons_kmap[flag & KMAP_MASK]) = val; +return SCPE_OK; +} + +/* Show keyboard map */ + +t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (sim_devices[0]->dradix == 16) + fprintf (st, "%s = %X\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK])); +else fprintf (st, "%s = %o\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK])); +return SCPE_OK; +} + +/* Set printable characters */ + +t_stat sim_set_pchar (int32 flag, char *cptr) +{ +DEVICE *dptr = sim_devices[0]; +uint32 val, rdx; +t_stat r; + +if ((cptr == NULL) || (*cptr == 0)) + return SCPE_2FARG; +if (dptr->dradix == 16) rdx = 16; +else rdx = 8; +val = (uint32) get_uint (cptr, rdx, 0xFFFFFFFF, &r); +if ((r != SCPE_OK) || + ((val & 0x00002400) == 0)) + return SCPE_ARG; +sim_tt_pchar = val; +return SCPE_OK; +} + +/* Show printable characters */ + +t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (sim_devices[0]->dradix == 16) + fprintf (st, "pchar mask = %X\n", sim_tt_pchar); +else fprintf (st, "pchar mask = %o\n", sim_tt_pchar); +return SCPE_OK; +} + +/* Set log routine */ + +t_stat sim_set_logon (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; + +if ((cptr == NULL) || (*cptr == 0)) /* need arg */ + return SCPE_2FARG; +cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */ +if (*cptr != 0) /* now eol? */ + return SCPE_2MARG; +sim_set_logoff (0, NULL); /* close cur log */ +sim_log = sim_fopen (gbuf, "a"); /* open log */ +if (sim_log == NULL) /* error? */ + return SCPE_OPENERR; +if (!sim_quiet) + printf ("Logging to file \"%s\"\n", gbuf); +fprintf (sim_log, "Logging to file \"%s\"\n", gbuf); /* start of log */ +return SCPE_OK; +} + +/* Set nolog routine */ + +t_stat sim_set_logoff (int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) /* now eol? */ + return SCPE_2MARG; +if (sim_log == NULL) /* no log? */ + return SCPE_OK; +if (!sim_quiet) + printf ("Log file closed\n"); +fprintf (sim_log, "Log file closed\n"); /* close log */ +fclose (sim_log); +sim_log = NULL; +return SCPE_OK; +} + +/* Show log status */ + +t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) + return SCPE_2MARG; +if (sim_log) + fputs ("Logging enabled\n", st); +else fputs ("Logging disabled\n", st); +return SCPE_OK; +} + +/* Set debug routine */ + +t_stat sim_set_debon (int32 flag, char *cptr) +{ +char *tptr, gbuf[CBUFSIZE]; + +if ((cptr == NULL) || (*cptr == 0)) /* too few arguments? */ + return SCPE_2FARG; +tptr = get_glyph (cptr, gbuf, 0); /* get file name */ +if (*tptr != 0) /* now eol? */ + return SCPE_2MARG; +sim_set_deboff (0, NULL); /* close cur debug */ +if (strcmp (gbuf, "LOG") == 0) { /* debug to log? */ + if (sim_log == NULL) /* any log? */ + return SCPE_ARG; + sim_deb = sim_log; + } +else if (strcmp (gbuf, "STDOUT") == 0) /* debug to stdout? */ + sim_deb = stdout; +else if (strcmp (gbuf, "STDERR") == 0) /* debug to stderr? */ + sim_deb = stderr; +else { + cptr = get_glyph_nc (cptr, gbuf, 0); /* reparse */ + sim_deb = sim_fopen (gbuf, "a"); /* open debug */ + if (sim_deb == NULL) /* error? */ + return SCPE_OPENERR; + sim_deb_close = 1; /* need close */ + } +if (!sim_quiet) + printf ("Debug output to \"%s\"\n", gbuf); +if (sim_log) + fprintf (sim_log, "Debug output to \"%s\"\n", gbuf); +return SCPE_OK; +} + +/* Set nodebug routine */ + +t_stat sim_set_deboff (int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) /* now eol? */ + return SCPE_2MARG; +if (sim_deb == NULL) /* no log? */ + return SCPE_OK; +if (!sim_quiet) + printf ("Debug output disabled\n"); +if (sim_log) + fprintf (sim_log, "Debug output disabled\n"); +if (sim_deb_close) /* close if needed */ + fclose (sim_deb); +sim_deb_close = 0; +sim_deb = NULL; +return SCPE_OK; +} + +/* Show debug routine */ + +t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) + return SCPE_2MARG; +if (sim_deb) + fputs ("Debug output enabled\n", st); +else fputs ("Debug output disabled\n", st); +return SCPE_OK; +} + +/* Set console to Telnet port */ + +t_stat sim_set_telnet (int32 flg, char *cptr) +{ +if ((cptr == NULL) || (*cptr == 0)) /* too few arguments? */ + return SCPE_2FARG; +if (sim_con_tmxr.master) /* already open? */ + return SCPE_ALATT; +return tmxr_open_master (&sim_con_tmxr, cptr); /* open master socket */ +} + +/* Close console Telnet port */ + +t_stat sim_set_notelnet (int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) /* too many arguments? */ + return SCPE_2MARG; +if (sim_con_tmxr.master == 0) /* ignore if already closed */ + return SCPE_OK; +return tmxr_close_master (&sim_con_tmxr); /* close master socket */ +} + +/* Show console Telnet status */ + +t_stat sim_show_telnet (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) + return SCPE_2MARG; +if (sim_con_tmxr.master == 0) + fprintf (st, "Connected to console window\n"); +else if (sim_con_ldsc.conn == 0) + fprintf (st, "Listening on port %d\n", sim_con_tmxr.port); +else { + fprintf (st, "Listening on port %d, connected to socket %d\n", + sim_con_tmxr.port, sim_con_ldsc.conn); + tmxr_fconns (st, &sim_con_ldsc, -1); + tmxr_fstats (st, &sim_con_ldsc, -1); + } +return SCPE_OK; +} + +/* Check connection before executing */ + +t_stat sim_check_console (int32 sec) +{ +int32 c, i; + +if (sim_con_tmxr.master == 0) /* not Telnet? done */ + return SCPE_OK; +if (sim_con_ldsc.conn) { /* connected? */ + tmxr_poll_rx (&sim_con_tmxr); /* poll (check disconn) */ + if (sim_con_ldsc.conn) /* still connected? */ + return SCPE_OK; + } +for (i = 0; i < sec; i++) { /* loop */ + if (tmxr_poll_conn (&sim_con_tmxr) >= 0) { /* poll connect */ + sim_con_ldsc.rcve = 1; /* rcv enabled */ + if (i) { /* if delayed */ + printf ("Running\n"); /* print transition */ + fflush (stdout); + } + return SCPE_OK; /* ready to proceed */ + } + c = sim_os_poll_kbd (); /* check for stop char */ + if ((c == SCPE_STOP) || stop_cpu) + return SCPE_STOP; + if ((i % 10) == 0) { /* Status every 10 sec */ + printf ("Waiting for console Telnet connection\n"); + fflush (stdout); + } + sim_os_sleep (1); /* wait 1 second */ + } +return SCPE_TTMO; /* timed out */ +} + +/* Poll for character */ + +t_stat sim_poll_kbd (void) +{ +int32 c; + +c = sim_os_poll_kbd (); /* get character */ +if ((c == SCPE_STOP) || (sim_con_tmxr.master == 0)) /* ^E or not Telnet? */ + return c; /* in-window */ +if (sim_con_ldsc.conn == 0) /* no Telnet conn? */ + return SCPE_LOST; +tmxr_poll_rx (&sim_con_tmxr); /* poll for input */ +if (c = tmxr_getc_ln (&sim_con_ldsc)) /* any char? */ + return (c & (SCPE_BREAK | 0377)) | SCPE_KFLAG; +return SCPE_OK; +} + +/* Output character */ + +t_stat sim_putchar (int32 c) +{ +if (sim_log) /* log file? */ + fputc (c, sim_log); +if (sim_con_tmxr.master == 0) /* not Telnet? */ + return sim_os_putchar (c); /* in-window version */ +if (sim_con_ldsc.conn == 0) /* no Telnet conn? */ + return SCPE_LOST; +tmxr_putc_ln (&sim_con_ldsc, c); /* output char */ +tmxr_poll_tx (&sim_con_tmxr); /* poll xmt */ +return SCPE_OK; +} + +t_stat sim_putchar_s (int32 c) +{ +t_stat r; + +if (sim_log) fputc (c, sim_log); /* log file? */ +if (sim_con_tmxr.master == 0) /* not Telnet? */ + return sim_os_putchar (c); /* in-window version */ +if (sim_con_ldsc.conn == 0) /* no Telnet conn? */ + return SCPE_LOST; +if (sim_con_ldsc.xmte == 0) /* xmt disabled? */ + r = SCPE_STALL; +else r = tmxr_putc_ln (&sim_con_ldsc, c); /* no, Telnet output */ +tmxr_poll_tx (&sim_con_tmxr); /* poll xmt */ +return r; /* return status */ +} + +/* Input character processing */ + +int32 sim_tt_inpcvt (int32 c, uint32 mode) +{ +uint32 md = mode & TTUF_M_MODE; + +if (md != TTUF_MODE_8B) { + c = c & 0177; + if (md == TTUF_MODE_UC) { + if (islower (c)) + c = toupper (c); + if (mode & TTUF_KSR) + c = c | 0200; + } + } +else c = c & 0377; +return c; +} + +/* Output character processing */ + +int32 sim_tt_outcvt (int32 c, uint32 mode) +{ +uint32 md = mode & TTUF_M_MODE; + +if (md != TTUF_MODE_8B) { + c = c & 0177; + if (md == TTUF_MODE_UC) { + if (islower (c)) + c = toupper (c); + if ((mode & TTUF_KSR) && (c >= 0140)) + return -1; + } + if (((md == TTUF_MODE_UC) || (md == TTUF_MODE_7P)) && + ((c == 0177) || + ((c < 040) && !((sim_tt_pchar >> c) & 1)))) + return -1; + } +else c = c & 0377; +return c; +} + +/* VMS routines, from Ben Thomas, with fixes from Robert Alan Byer */ + +#if defined (VMS) + +#if defined(__VAX) +#define sys$assign SYS$ASSIGN +#define sys$qiow SYS$QIOW +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define EFN 0 +uint32 tty_chan = 0; + +typedef struct { + unsigned short sense_count; + unsigned char sense_first_char; + unsigned char sense_reserved; + unsigned int stat; + unsigned int stat2; } SENSE_BUF; + +typedef struct { + unsigned short status; + unsigned short count; + unsigned int dev_status; } IOSB; + +SENSE_BUF cmd_mode = { 0 }; +SENSE_BUF run_mode = { 0 }; + +t_stat sim_ttinit (void) +{ +unsigned int status; +IOSB iosb; +$DESCRIPTOR (terminal_device, "tt"); + +status = sys$assign (&terminal_device, &tty_chan, 0, 0); +if (status != SS$_NORMAL) + return SCPE_TTIERR; +status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE, &iosb, 0, 0, + &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) + return SCPE_TTIERR; +run_mode = cmd_mode; +run_mode.stat = cmd_mode.stat | TT$M_NOECHO & ~(TT$M_HOSTSYNC | TT$M_TTSYNC); +run_mode.stat2 = cmd_mode.stat2 | TT2$M_PASTHRU; +return SCPE_OK; +} + +t_stat sim_ttrun (void) +{ +unsigned int status; +IOSB iosb; + +status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0, + &run_mode, sizeof (run_mode), 0, 0, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) + return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ +unsigned int status; +IOSB iosb; + +status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0, + &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) + return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat sim_ttclose (void) +{ +return sim_ttcmd (); +} + +t_stat sim_os_poll_kbd (void) +{ +unsigned int status, term[2]; +unsigned char buf[4]; +IOSB iosb; +SENSE_BUF sense; + +term[0] = 0; term[1] = 0; +status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE | IO$M_TYPEAHDCNT, &iosb, + 0, 0, &sense, 8, 0, term, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) + return SCPE_TTIERR; +if (sense.sense_count == 0) return SCPE_OK; +term[0] = 0; term[1] = 0; +status = sys$qiow (EFN, tty_chan, + IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO, + &iosb, 0, 0, buf, 1, 0, term, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) + return SCPE_OK; +if (buf[0] == sim_int_char) return SCPE_STOP; +if (sim_brk_char && (buf[0] == sim_brk_char)) + return SCPE_BREAK; +return (buf[0] | SCPE_KFLAG); +} + +t_stat sim_os_putchar (int32 out) +{ +unsigned int status; +char c; +IOSB iosb; + +c = out; +status = sys$qiow (EFN, tty_chan, IO$_WRITELBLK | IO$M_NOFORMAT, + &iosb, 0, 0, &c, 1, 0, 0, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) + return SCPE_TTOERR; +return SCPE_OK; +} + +/* Win32 routines */ + +#elif defined (_WIN32) + +#include +#include +#include +#include +#define RAW_MODE 0 +static HANDLE std_input; +static DWORD saved_mode; + +t_stat sim_ttinit (void) +{ +std_input = GetStdHandle (STD_INPUT_HANDLE); +if ((std_input == INVALID_HANDLE_VALUE) || + !GetConsoleMode (std_input, &saved_mode)) + return SCPE_TTYERR; +return SCPE_OK; +} + +t_stat sim_ttrun (void) +{ +if (!GetConsoleMode(std_input, &saved_mode) || + !SetConsoleMode(std_input, RAW_MODE)) + return SCPE_TTYERR; +if (sim_log) { + fflush (sim_log); + _setmode (_fileno (sim_log), _O_BINARY); + } +SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); +return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ +if (sim_log) { + fflush (sim_log); + _setmode (_fileno (sim_log), _O_TEXT); + } +SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_NORMAL); +if (!SetConsoleMode(std_input, saved_mode)) return SCPE_TTYERR; +return SCPE_OK; +} + +t_stat sim_ttclose (void) +{ +return SCPE_OK; +} + +t_stat sim_os_poll_kbd (void) +{ +int c; + +if (!_kbhit ()) + return SCPE_OK; +c = _getch (); +if ((c & 0177) == sim_del_char) + c = 0177; +if ((c & 0177) == sim_int_char) + return SCPE_STOP; +if (sim_brk_char && ((c & 0177) == sim_brk_char)) + return SCPE_BREAK; +return c | SCPE_KFLAG; +} + +t_stat sim_os_putchar (int32 c) +{ +if (c != 0177) + _putch (c); +return SCPE_OK; +} + +/* OS/2 routines, from Bruce Ray and Holger Veit */ + +#elif defined (__OS2__) + +#include + +t_stat sim_ttinit (void) +{ +return SCPE_OK; +} + +t_stat sim_ttrun (void) +{ +return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ +return SCPE_OK; +} + +t_stat sim_ttclose (void) +{ +return SCPE_OK; +} + +t_stat sim_os_poll_kbd (void) +{ +int c; + +#if defined (__EMX__) +switch (c = _read_kbd(0,0,0)) { /* EMX has _read_kbd */ + + case -1: /* no char*/ + return SCPE_OK; + + case 0: /* char pending */ + c = _read_kbd(0,1,0); + break; + + default: /* got char */ + break; + } +#else +if (!kbhit ()) + return SCPE_OK; +c = getch(); +#endif +if ((c & 0177) == sim_del_char) + c = 0177; +if ((c & 0177) == sim_int_char) + return SCPE_STOP; +if (sim_brk_char && ((c & 0177) == sim_brk_char)) + return SCPE_BREAK; +return c | SCPE_KFLAG; +} + +t_stat sim_os_putchar (int32 c) +{ +if (c != 0177) { +#if defined (__EMX__) + putchar (c); +#else + putch (c); +#endif + fflush (stdout); + } +return SCPE_OK; +} + +/* Metrowerks CodeWarrior Macintosh routines, from Louis Chretien and + Peter Schorn */ + +#elif defined (__MWERKS__) && defined (macintosh) + +#include +#include +#include +#include +#include +#include +#include +#include + +/* function prototypes */ + +Boolean SIOUXIsAppWindow(WindowPtr window); +void SIOUXDoMenuChoice(long menuValue); +void SIOUXUpdateMenuItems(void); +void SIOUXUpdateScrollbar(void); +int ps_kbhit(void); +int ps_getch(void); + +extern char sim_name[]; +extern pSIOUXWin SIOUXTextWindow; +static CursHandle iBeamCursorH = NULL; /* contains the iBeamCursor */ + +static void updateCursor(void) { + WindowPtr window; + window = FrontWindow(); + if (SIOUXIsAppWindow(window)) { + GrafPtr savePort; + Point localMouse; + GetPort(&savePort); + SetPort(window); +#if TARGET_API_MAC_CARBON + GetGlobalMouse(&localMouse); +#else + localMouse = LMGetMouseLocation(); +#endif + GlobalToLocal(&localMouse); + if (PtInRect(localMouse, &(*SIOUXTextWindow->edit)->viewRect) && iBeamCursorH) { + SetCursor(*iBeamCursorH); + } + else { + SetCursor(&qd.arrow); + } + TEIdle(SIOUXTextWindow->edit); + SetPort(savePort); + } + else { + SetCursor(&qd.arrow); + TEIdle(SIOUXTextWindow->edit); + } + return; +} + +int ps_kbhit(void) { + EventRecord event; + int c; + updateCursor(); + SIOUXUpdateScrollbar(); + while (GetNextEvent(updateMask | osMask | mDownMask | mUpMask | activMask | + highLevelEventMask | diskEvt, &event)) { + SIOUXHandleOneEvent(&event); + } + if (SIOUXQuitting) { + exit(1); + } + if (EventAvail(keyDownMask,&event)) { + c = event.message&charCodeMask; + if ((event.modifiers & cmdKey) && (c > 0x20)) { + GetNextEvent(keyDownMask, &event); + SIOUXHandleOneEvent(&event); + if (SIOUXQuitting) { + exit(1); + } + return false; + } + return true; + } + else { + return false; + } +} + +int ps_getch(void) { + int c; + EventRecord event; + fflush(stdout); + updateCursor(); + while(!GetNextEvent(keyDownMask,&event)) { + if (GetNextEvent(updateMask | osMask | mDownMask | mUpMask | activMask | + highLevelEventMask | diskEvt, &event)) { + SIOUXUpdateScrollbar(); + SIOUXHandleOneEvent(&event); + } + } + if (SIOUXQuitting) { + exit(1); + } + c = event.message&charCodeMask; + if ((event.modifiers & cmdKey) && (c > 0x20)) { + SIOUXUpdateMenuItems(); + SIOUXDoMenuChoice(MenuKey(c)); + } + if (SIOUXQuitting) { + exit(1); + } + return c; +} + +/* Note that this only works if the call to sim_ttinit comes before any output to the console */ + +t_stat sim_ttinit (void) { + int i; + /* this blank will later be replaced by the number of characters */ + char title[50] = " "; + unsigned char ptitle[50]; + SIOUXSettings.autocloseonquit = TRUE; + SIOUXSettings.asktosaveonclose = FALSE; + SIOUXSettings.showstatusline = FALSE; + SIOUXSettings.columns = 80; + SIOUXSettings.rows = 40; + SIOUXSettings.toppixel = 42; + SIOUXSettings.leftpixel = 6; + iBeamCursorH = GetCursor(iBeamCursor); + strcat(title, sim_name); + strcat(title, " Simulator"); + title[0] = strlen(title) - 1; /* Pascal string done */ + for (i = 0; i <= title[0]; i++) { /* copy to unsigned char */ + ptitle[i] = title[i]; + } + SIOUXSetTitle(ptitle); + return SCPE_OK; +} + +t_stat sim_ttrun (void) +{ +return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ +return SCPE_OK; +} + +t_stat sim_ttclose (void) +{ +return SCPE_OK; +} + +t_stat sim_os_poll_kbd (void) +{ +int c; + +if (!ps_kbhit ()) + return SCPE_OK; +c = ps_getch(); +if ((c & 0177) == sim_del_char) + c = 0177; +if ((c & 0177) == sim_int_char) return SCPE_STOP; +if (sim_brk_char && ((c & 0177) == sim_brk_char)) + return SCPE_BREAK; +return c | SCPE_KFLAG; +} + +t_stat sim_os_putchar (int32 c) +{ +if (c != 0177) { + putchar (c); + fflush (stdout); + } +return SCPE_OK; +} + +/* BSD UNIX routines */ + +#elif defined (BSDTTY) + +#include +#include +#include + +struct sgttyb cmdtty,runtty; /* V6/V7 stty data */ +struct tchars cmdtchars,runtchars; /* V7 editing */ +struct ltchars cmdltchars,runltchars; /* 4.2 BSD editing */ +int cmdfl,runfl; /* TTY flags */ + +t_stat sim_ttinit (void) +{ +cmdfl = fcntl (0, F_GETFL, 0); /* get old flags and status */ +runfl = cmdfl | FNDELAY; +if (ioctl (0, TIOCGETP, &cmdtty) < 0) + return SCPE_TTIERR; +if (ioctl (0, TIOCGETC, &cmdtchars) < 0) + return SCPE_TTIERR; +if (ioctl (0, TIOCGLTC, &cmdltchars) < 0) + return SCPE_TTIERR; +runtty = cmdtty; /* initial run state */ +runtty.sg_flags = cmdtty.sg_flags & ~(ECHO|CRMOD) | CBREAK; +runtchars.t_intrc = sim_int_char; /* interrupt */ +runtchars.t_quitc = 0xFF; /* no quit */ +runtchars.t_startc = 0xFF; /* no host sync */ +runtchars.t_stopc = 0xFF; +runtchars.t_eofc = 0xFF; +runtchars.t_brkc = 0xFF; +runltchars.t_suspc = 0xFF; /* no specials of any kind */ +runltchars.t_dsuspc = 0xFF; +runltchars.t_rprntc = 0xFF; +runltchars.t_flushc = 0xFF; +runltchars.t_werasc = 0xFF; +runltchars.t_lnextc = 0xFF; +return SCPE_OK; /* return success */ +} + +t_stat sim_ttrun (void) +{ +runtchars.t_intrc = sim_int_char; /* in case changed */ +fcntl (0, F_SETFL, runfl); /* non-block mode */ +if (ioctl (0, TIOCSETP, &runtty) < 0) + return SCPE_TTIERR; +if (ioctl (0, TIOCSETC, &runtchars) < 0) + return SCPE_TTIERR; +if (ioctl (0, TIOCSLTC, &runltchars) < 0) + return SCPE_TTIERR; +nice (10); /* lower priority */ +return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ +nice (-10); /* restore priority */ +fcntl (0, F_SETFL, cmdfl); /* block mode */ +if (ioctl (0, TIOCSETP, &cmdtty) < 0) + return SCPE_TTIERR; +if (ioctl (0, TIOCSETC, &cmdtchars) < 0) + return SCPE_TTIERR; +if (ioctl (0, TIOCSLTC, &cmdltchars) < 0) + return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat sim_ttclose (void) +{ +return sim_ttcmd (); +} + +t_stat sim_os_poll_kbd (void) +{ +int status; +unsigned char buf[1]; + +status = read (0, buf, 1); +if (status != 1) return SCPE_OK; +if (sim_brk_char && (buf[0] == sim_brk_char)) + return SCPE_BREAK; +else return (buf[0] | SCPE_KFLAG); +} + +t_stat sim_os_putchar (int32 out) +{ +char c; + +c = out; +write (1, &c, 1); +return SCPE_OK; +} + +/* POSIX UNIX routines, from Leendert Van Doorn */ + +#else + +#include +#include + +struct termios cmdtty, runtty; +static int prior_norm = 1; + +t_stat sim_ttinit (void) +{ +if (!isatty (fileno (stdin))) /* skip if !tty */ + return SCPE_OK; +if (tcgetattr (0, &cmdtty) < 0) /* get old flags */ + return SCPE_TTIERR; +runtty = cmdtty; +runtty.c_lflag = runtty.c_lflag & ~(ECHO | ICANON); /* no echo or edit */ +runtty.c_oflag = runtty.c_oflag & ~OPOST; /* no output edit */ +runtty.c_iflag = runtty.c_iflag & ~ICRNL; /* no cr conversion */ +runtty.c_cc[VINTR] = sim_int_char; /* interrupt */ +runtty.c_cc[VQUIT] = 0; /* no quit */ +runtty.c_cc[VERASE] = 0; +runtty.c_cc[VKILL] = 0; +runtty.c_cc[VEOF] = 0; +runtty.c_cc[VEOL] = 0; +runtty.c_cc[VSTART] = 0; /* no host sync */ +runtty.c_cc[VSUSP] = 0; +runtty.c_cc[VSTOP] = 0; +#if defined (VREPRINT) +runtty.c_cc[VREPRINT] = 0; /* no specials */ +#endif +#if defined (VDISCARD) +runtty.c_cc[VDISCARD] = 0; +#endif +#if defined (VWERASE) +runtty.c_cc[VWERASE] = 0; +#endif +#if defined (VLNEXT) +runtty.c_cc[VLNEXT] = 0; +#endif +runtty.c_cc[VMIN] = 0; /* no waiting */ +runtty.c_cc[VTIME] = 0; +#if defined (VDSUSP) +runtty.c_cc[VDSUSP] = 0; +#endif +#if defined (VSTATUS) +runtty.c_cc[VSTATUS] = 0; +#endif +return SCPE_OK; +} + +t_stat sim_ttrun (void) +{ +if (!isatty (fileno (stdin))) /* skip if !tty */ + return SCPE_OK; +runtty.c_cc[VINTR] = sim_int_char; /* in case changed */ +if (tcsetattr (0, TCSAFLUSH, &runtty) < 0) + return SCPE_TTIERR; +if (prior_norm) { /* at normal pri? */ + errno = 0; + nice (10); /* try to lower pri */ + prior_norm = errno; /* if no error, done */ + } +return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ +if (!isatty (fileno (stdin))) /* skip if !tty */ + return SCPE_OK; +if (!prior_norm) { /* priority down? */ + errno = 0; + nice (-10); /* try to raise pri */ + prior_norm = (errno == 0); /* if no error, done */ + } +if (tcsetattr (0, TCSAFLUSH, &cmdtty) < 0) + return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat sim_ttclose (void) +{ +return sim_ttcmd (); +} + +t_stat sim_os_poll_kbd (void) +{ +int status; +unsigned char buf[1]; + +status = read (0, buf, 1); +if (status != 1) return SCPE_OK; +if (sim_brk_char && (buf[0] == sim_brk_char)) + return SCPE_BREAK; +else return (buf[0] | SCPE_KFLAG); +} + +t_stat sim_os_putchar (int32 out) +{ +char c; + +c = out; +write (1, &c, 1); +return SCPE_OK; +} + +#endif diff --git a/sim_ether.c b/sim_ether.c index 09bd134b..5a1fb115 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -49,8 +49,8 @@ networking is needed - there may be known workarounds. Define one of the two macros below to enable networking: - USE_NETWORK - Create statically linked network code - USE_SHARED - Create dynamically linked network code (_WIN32 only) + USE_NETWORK - Create statically linked network code + USE_SHARED - Create dynamically linked network code (_WIN32 only) ------------------------------------------------------------------------------ @@ -118,13 +118,26 @@ Using the threaded approach may require special compile and/or link time switches (i.e. -lpthread or -pthread, etc.) Consult the documentation for your platform as - needed. - MUST_DO_SELECT - Specifies that when USE_READER_THREAD is active, that + needed. Although this may be 'less efficient' than the + non-threaded approach, the efficiency is an overall system + efficiency not necessarily a simulator efficiency. This + means that work is removed from the thread executing + simulated instructions so the simulated system will most + likely run faster (given that modern host CPUs are + multi-core and have someplace to do this work in parallel). + MUST_DO_SELECT - Specifies that, when USE_READER_THREAD is active, select() should be used to determin when available packets are ready for reading. Otherwise, we depend on the libpcap/kernel packet timeout specified on pcap_open_live. If USE_READER_THREAD is not set, then MUST_DO_SELECT is irrelevant + USE_TAP_NETWORK - Specifies that support for tap networking should be + included. This can be leveraged, along with OS bridging + capabilities to share a single LAN interface. This + allows device names of the form tap:tap0 to be specified + at open time. This functionality is only useful/needed + on *nix platforms since native sharing of Windows NIC + devices works with no external magic. NEED_PCAP_SENDPACKET - Specifies that you are using an older version of libpcap @@ -138,6 +151,89 @@ Modification history: + 09-Jan-11 MP Fixed missing crc data when USE_READER_THREAD is defined and + crc's are needed (only the pdp11_xu) + 16-Dec-10 MP added priority boost for read and write threads when + USE_READER_THREAD does I/O in separate threads. This helps + throughput since it allows these I/O bound threads to preempt + the main thread (which is executing simulated instructions). + 09-Dec-10 MP allowed more flexible parsing of MAC address strings + 09-Dec-10 MP Added support to determine if network address conflicts exist + 07-Dec-10 MP Reworked DECnet self detection to the more general approach + of loopback self when a Physical Address is being set. + 04-Dec-10 MP Changed eth_write to do nonblocking writes when + USE_READER_THREAD is defined. + 20-Aug-10 TVO Fix for Mac OSX 10.6 + 17-Jun-10 MP Fixed bug in the AUTODIN II hash filtering. + 14-Jun-10 MP Added support for integrated Tap networking interfaces on BSD + platforms. + 13-Jun-10 MP Added support for integrated Tap networking interfaces on Linux + platforms. + 31-May-10 MP Added support for more TOE (TCP Offload Engine) features for IPv4 + network traffic from the host and/or from hosts on the LAN. These + new TOE features are: LSO (Large Send Offload) and Jumbo packet + fragmentation support. These features allow a simulated network + device to suuport traffic when a host leverages a NIC's Large + Send Offload capabilities to fregment and/or segment outgoing + network traffic. Additionally a simulated network device can + reasonably exist on a LAN which is configured to use Jumbo frames. + 21-May-10 MP Added functionslity to fixup IP header checksums to accomodate + packets from a host with a NIC which has TOE (TCP Offload Engine) + enabled which is expected to implement the checksum computations + in hardware. Since we catch packets before they arrive at the + NIC the expected checksum insertions haven't been performed yet. + This processing is only done for packets sent from the hoat to + the guest we're supporting. In general this will be a relatively + small number of packets so it is done for all IP frame packets + coming from the hoat to the guest. In order to make the + determination of packets specifically arriving from the host we + need to know the hardware MAC address of the host NIC. Currently + determining a NIC's MAC address is relatively easy on Windows. + The non-windows code works on linux and may work on other *nix + platforms either as is or with slight modifications. The code, + as implemented, only messes with this activity if the host + interface MAC address can be determined. + 20-May-10 MP Added general support to deal with receiving packets smaller + than ETH_MIN_PACKET in length. These come from packets + looped back by some bridging mechanism and need to be padded + to the minimum frame size. A real NIC won't pass us any + packets like that. This fix belongs here since this layer + is responsible for interfacing to they physical layer + devices, AND it belongs here to get CRC processing right. + 05-Mar-08 MP Added optional multicast filtering support for doing + LANCE style AUTODIN II based hashed filtering. + 07-Feb-08 MP Added eth_show_dev to display ethernet state + Changed the return value from eth_read to return whether + or not a packet was read. No existing callers used or + checked constant return value that previously was being + supplied. + 29-Jan-08 MP Added eth_set_async to provide a mechanism (when + USE_READER_THREAD is enabled) to allow packet reception + to dynamically update the simulator event queue and + potentially avoid polling for I/O. This provides a minimal + overhead (no polling) maximal responsiveness for network + activities. + 29-Jan-08 MP Properly sequenced activities in eth_close to avoid a race + condition when USE_READER_THREAD is enabled. + 25-Jan-08 MP Changed the following when USE_READER_THREAD is enabled: + - Fixed bug when the simulated device doesn't need crc + in packet data which is read. + - Added call to pcap_setmintocopy to minimize packet + delivery latencies. + - Added ethq_destroy and used it to avoid a memory leak in + eth_close. + - Properly cleaned up pthread mutexes in eth_close. + Migrated to using sim_os_ms_sleep for a delay instead of + a call to select(). + Fixed the bpf filter used when no traffic is to be matched. + Reworked eth_add_packet_crc32 implementation to avoid an + extra buffer copy while reading packets. + Fixedup #ifdef's relating to USE_SHARED so that setting + USE_SHARED or USE_NETWORK will build a working network + environment. + 23-Jan-08 MP Reworked eth_packet_trace and eth_packet_trace_ex to allow + only output ethernet header+crc and provide a mechanism for + the simulated device to display full packet data debugging. 17-May-07 DTH Fixed non-ethernet device removal loop (from Naoki Hamada) 15-May-07 DTH Added dynamic loading of wpcap.dll; Corrected exceed max index bug in ethX lookup @@ -252,36 +348,23 @@ extern FILE *sim_log; t_stat eth_mac_scan (ETH_MAC* mac, char* strmac) { - int i, j; - short unsigned int num; - char cptr[18]; - int len = strlen(strmac); + int a0, a1, a2, a3, a4, a5; const ETH_MAC zeros = {0,0,0,0,0,0}; const ETH_MAC ones = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; ETH_MAC newmac; - /* format of string must be 6 double-digit hex bytes with valid separators - ideally, this mac scanner could allow more flexible formatting later */ - if (len != 17) return SCPE_ARG; - - /* copy string to local storage for mangling */ - strcpy(cptr, strmac); - - /* make sure byte separators are OK */ - for (i=2; i 0xFF) || (a1 > 0xFF) || (a2 > 0xFF) || (a3 > 0xFF) || (a4 > 0xFF) || (a5 > 0xFF)) + return SCPE_ARG; + newmac[0] = a0; + newmac[1] = a1; + newmac[2] = a2; + newmac[3] = a3; + newmac[4] = a4; + newmac[5] = a5; /* final check - mac cannot be broadcast or multicast address */ if (!memcmp(newmac, zeros, sizeof(ETH_MAC)) || /* broadcast */ @@ -354,22 +437,48 @@ uint32 eth_crc32(uint32 crc, const void* vbuf, size_t len) const unsigned char* buf = (const unsigned char*)vbuf; crc ^= mask; + while (len > 8) { + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + len -= 8; + } while (0 != len--) crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; return(crc ^ mask); } -void eth_add_crc32(ETH_PACK* packet) +int eth_get_packet_crc32_data(const uint8 *msg, int len, uint8 *crcdata) { - if (packet->len <= ETH_MAX_PACKET) { - uint32 crc = eth_crc32(0, packet->msg, packet->len); /* calculate CRC */ + int crc_len; + + if (len <= ETH_MAX_PACKET) { + uint32 crc = eth_crc32(0, msg, len); /* calculate CRC */ uint32 ncrc = htonl(crc); /* CRC in network order */ int size = sizeof(ncrc); /* size of crc field */ - memcpy(&packet->msg[packet->len], &ncrc, size); /* append crc to packet */ - packet->crc_len = packet->len + size; /* set packet crc length */ + memcpy(crcdata, &ncrc, size); /* append crc to packet */ + crc_len = len + size; /* set packet crc length */ } else { - packet->crc_len = 0; /* appending crc would destroy packet */ + crc_len = 0; /* appending crc would destroy packet */ } + return crc_len; +} + +int eth_add_packet_crc32(uint8 *msg, int len) +{ + int crc_len; + + if (len <= ETH_MAX_PACKET) { + crc_len = eth_get_packet_crc32_data(msg, len, &msg[len]);/* append crc to packet */ + } else { + crc_len = 0; /* appending crc would destroy packet */ + } + return crc_len; } void eth_setcrc(ETH_DEV* dev, int need_crc) @@ -377,18 +486,18 @@ void eth_setcrc(ETH_DEV* dev, int need_crc) dev->need_crc = need_crc; } -void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, char* txt, int dmp) +void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, char* txt, int detail, uint32 reason) { - if (dev->dptr->dctrl & dev->dbit) { + if (dev->dptr->dctrl & reason) { char src[20]; char dst[20]; unsigned short* proto = (unsigned short*) &msg[12]; uint32 crc = eth_crc32(0, msg, len); eth_mac_fmt((ETH_MAC*)&msg[0], dst); eth_mac_fmt((ETH_MAC*)&msg[6], src); - sim_debug(dev->dbit, dev->dptr, "%s dst: %s src: %s proto: 0x%04X len: %d crc: %X\n", + sim_debug(reason, dev->dptr, "%s dst: %s src: %s proto: 0x%04X len: %d crc: %X\n", txt, dst, src, ntohs(*proto), len, crc); - if (dmp) { + if (detail) { int i, same, group, sidx, oidx; char outbuf[80], strbuf[18]; static char hex[] = "0123456789ABCDEF"; @@ -398,7 +507,7 @@ void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, char* txt, int continue; } if (same > 0) { - sim_debug(dev->dbit, dev->dptr, "%04X thru %04X same as above\r\n", i-(16*same), i-1); + sim_debug(reason, dev->dptr, "%04X thru %04X same as above\n", i-(16*same), i-1); same = 0; } group = (((len - i) > 16) ? 16 : (len - i)); @@ -413,17 +522,17 @@ void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, char* txt, int } outbuf[oidx] = '\0'; strbuf[sidx] = '\0'; - sim_debug(dev->dbit, dev->dptr, "%04X%-48s %s\r\n", i, outbuf, strbuf); + sim_debug(reason, dev->dptr, "%04X%-48s %s\n", i, outbuf, strbuf); } if (same > 0) - sim_debug(dev->dbit, dev->dptr, "%04X thru %04X same as above\r\n", i-(16*same), len-1); + sim_debug(reason, dev->dptr, "%04X thru %04X same as above\n", i-(16*same), len-1); } } } void eth_packet_trace(ETH_DEV* dev, const uint8 *msg, int len, char* txt) { - eth_packet_trace_ex(dev, msg, len, txt, 1/*len > ETH_MAX_PACKET*/); + eth_packet_trace_ex(dev, msg, len, txt, 0, dev->dbit); } char* eth_getname(int number, char* name) @@ -441,11 +550,11 @@ char* eth_getname_bydesc(char* desc, char* name) ETH_LIST list[ETH_MAX_DEVICE]; int count = eth_devices(ETH_MAX_DEVICE, list); int i; - int j=strlen(desc); + size_t j=strlen(desc); for (i=0; i min) min = len; for (i=0; iitem) { - size_t size = sizeof(struct eth_item) * max; - que->max = max; - que->item = (struct eth_item *) malloc(size); - if (que->item) { - /* init dynamic memory */ - memset(que->item, 0, size); - } else { + que->item = (struct eth_item *) calloc(max, sizeof(struct eth_item)); + if (!que->item) { /* failed to allocate memory */ char* msg = "EthQ: failed to allocate dynamic queue[%d]\r\n"; printf(msg, max); if (sim_log) fprintf(sim_log, msg, max); return SCPE_MEM; }; + que->max = max; + }; + ethq_clear(que); + return SCPE_OK; +} + +t_stat ethq_destroy(ETH_QUE* que) +{ + /* release dynamic queue if it exists */ + ethq_clear(que); + que->max = 0; + if (que->item) { + free(que->item); + que->item = NULL; }; return SCPE_OK; } @@ -557,7 +677,7 @@ void ethq_clear(ETH_QUE* que) /* clear packet array */ memset(que->item, 0, sizeof(struct eth_item) * que->max); /* clear rest of structure */ - que->count = que->head = que->tail = que->loss = que->high = 0; + que->count = que->head = que->tail = 0; } void ethq_remove(ETH_QUE* que) @@ -572,7 +692,7 @@ void ethq_remove(ETH_QUE* que) } } -void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status) +void ethq_insert_data(ETH_QUE* que, int32 type, const uint8 *data, int used, int len, int crc_len, const uint8 *crc_data, int32 status) { struct eth_item* item; @@ -598,39 +718,58 @@ void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status) /* set information in (new) tail item */ item = &que->item[que->tail]; item->type = type; - item->packet.len = pack->len; - item->packet.used = 0; - item->packet.crc_len = pack->crc_len; - memcpy(item->packet.msg, pack->msg, ((pack->len > pack->crc_len) ? pack->len : pack->crc_len)); + item->packet.len = len; + item->packet.used = used; + item->packet.crc_len = crc_len; + memcpy(item->packet.msg, data, ((len > crc_len) ? len : crc_len)); + if (crc_data && (crc_len > len)) + memcpy(&item->packet.msg[len], crc_data, ETH_CRC_SIZE); item->packet.status = status; } +void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status) +{ + ethq_insert_data(que, type, pack->msg, pack->used, pack->len, pack->crc_len, NULL, status); +} + /*============================================================================*/ /* Non-implemented versions */ /*============================================================================*/ -#if !defined (USE_NETWORK) && !defined(USE_SHARED) +#if !defined (USE_NETWORK) && (!defined (_WIN32) || !defined (USE_SHARED)) t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) {return SCPE_NOFNC;} t_stat eth_close (ETH_DEV* dev) {return SCPE_NOFNC;} +t_stat eth_check_address_conflict (ETH_DEV* dev, + ETH_MAC* const mac) + {return SCPE_NOFNC;} +t_stat eth_set_async (ETH_DEV *dev, int latency) + {return SCPE_NOFNC;} +t_stat eth_clr_async (ETH_DEV *dev) + {return SCPE_NOFNC;} t_stat eth_write (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) {return SCPE_NOFNC;} -t_stat eth_read (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) +int eth_read (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) {return SCPE_NOFNC;} -t_stat eth_filter (ETH_DEV* dev, int addr_count, ETH_MAC* addresses, +t_stat eth_filter (ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous) {return SCPE_NOFNC;} +t_stat eth_filter_hash (ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, + ETH_BOOL all_multicast, ETH_BOOL promiscuous, ETH_MULTIHASH* const hash) + {return SCPE_NOFNC;} int eth_devices (int max, ETH_LIST* dev) {return -1;} -#else /* endif unimplemented */ +void eth_show_dev (FILE* st, ETH_DEV* dev) + {} +#else /* endif unimplemented */ /*============================================================================*/ /* WIN32, Linux, and xBSD routines use WinPcap and libpcap packages */ /* OpenVMS Alpha uses a WinPcap port and an associated execlet */ /*============================================================================*/ -#if defined (xBSD) && !defined(__APPLE__) +#if defined (xBSD) || defined(__APPLE__) #include #include #endif /* xBSD */ @@ -638,6 +777,20 @@ int eth_devices (int max, ETH_LIST* dev) #include #include +#ifdef USE_TAP_NETWORK +#if defined(__linux) +#include +#include +#include +#elif defined(USE_BSDTUNTAP) +#include +#include +#include +#else /* We don't know how to do this on the current platform */ +#undef USE_TAP_NETWORK +#endif +#endif /* USE_TAP_NETWORK */ + /* Allows windows to look up user-defined adapter names */ #if defined(_WIN32) #include @@ -648,8 +801,8 @@ int eth_devices (int max, ETH_LIST* dev) Etherial/WireShark capture_pcap.c */ /* Dynamic DLL load variables */ -static HINSTANCE hDll = 0; /* handle to DLL */ -static int dll_loaded = 0; /* 0=not loaded, 1=loaded, 2=DLL load failed, 3=Func load failed */ +static HINSTANCE hDll = 0; /* handle to DLL */ +static int dll_loaded = 0; /* 0=not loaded, 1=loaded, 2=DLL load failed, 3=Func load failed */ static char* no_wpcap = "wpcap load failure"; /* define pointers to pcap functions needed */ @@ -663,164 +816,186 @@ static void (*p_pcap_freecode) (struct bpf_program *); static char* (*p_pcap_geterr) (pcap_t *); static int (*p_pcap_lookupnet) (const char *, bpf_u_int32 *, bpf_u_int32 *, char *); static pcap_t* (*p_pcap_open_live) (const char *, int, int, int, char *); +static int (*p_pcap_setmintocopy) (pcap_t* handle, int); +static HANDLE (*p_pcap_getevent) (pcap_t *); static int (*p_pcap_sendpacket) (pcap_t* handle, const u_char* msg, int len); static int (*p_pcap_setfilter) (pcap_t *, struct bpf_program *); static char* (*p_pcap_lib_version) (void); /* load function pointer from DLL */ void load_function(char* function, void** func_ptr) { - *func_ptr = GetProcAddress(hDll, function); - if (*func_ptr == 0) { - char* msg = "Eth: Failed to find function '%s' in wpcap.dll\r\n"; - printf (msg, function); - if (sim_log) fprintf (sim_log, msg, function); - dll_loaded = 3; - } + *func_ptr = GetProcAddress(hDll, function); + if (*func_ptr == 0) { + char* msg = "Eth: Failed to find function '%s' in wpcap.dll\r\n"; + + printf (msg, function); + if (sim_log) fprintf (sim_log, msg, function); + dll_loaded = 3; + } } /* load wpcap.dll as required */ int load_wpcap(void) { - switch(dll_loaded) { - case 0: /* not loaded */ + switch(dll_loaded) { + case 0: /* not loaded */ /* attempt to load DLL */ - hDll = LoadLibrary(TEXT("wpcap.dll")); - if (hDll == 0) { - /* failed to load DLL */ - char* msg = "Eth: Failed to load wpcap.dll\r\n"; - char* msg2 = "Eth: You must install WinPcap 4.x to use networking\r\n"; - printf (msg); - printf (msg2); - if (sim_log) { - fprintf (sim_log, msg); - fprintf (sim_log, msg2); - } - dll_loaded = 2; - break; - } else { - /* DLL loaded OK */ - dll_loaded = 1; - } + hDll = LoadLibrary(TEXT("wpcap.dll")); + if (hDll == 0) { + /* failed to load DLL */ + char* msg = "Eth: Failed to load wpcap.dll\r\n"; + char* msg2 = "Eth: You must install WinPcap 4.x to use networking\r\n"; - /* load required functions; sets dll_load=3 on error */ - load_function("pcap_close", (void**) &p_pcap_close); - load_function("pcap_compile", (void**) &p_pcap_compile); - load_function("pcap_datalink", (void**) &p_pcap_datalink); - load_function("pcap_dispatch", (void**) &p_pcap_dispatch); - load_function("pcap_findalldevs", (void**) &p_pcap_findalldevs); - load_function("pcap_freealldevs", (void**) &p_pcap_freealldevs); - load_function("pcap_freecode", (void**) &p_pcap_freecode); - load_function("pcap_geterr", (void**) &p_pcap_geterr); - load_function("pcap_lookupnet", (void**) &p_pcap_lookupnet); - load_function("pcap_open_live", (void**) &p_pcap_open_live); - load_function("pcap_sendpacket", (void**) &p_pcap_sendpacket); - load_function("pcap_setfilter", (void**) &p_pcap_setfilter); - load_function("pcap_lib_version", (void**) &p_pcap_lib_version); + printf (msg); + printf (msg2); + if (sim_log) { + fprintf (sim_log, msg); + fprintf (sim_log, msg2); + } + dll_loaded = 2; + break; + } else { + /* DLL loaded OK */ + dll_loaded = 1; + } - if (dll_loaded == 1) { - /* log successful load */ - char* version = p_pcap_lib_version(); - printf("%s\n", version); - if (sim_log) - fprintf(sim_log, "%s\n", version); - } - break; - default: /* loaded or failed */ - break; - } - return (dll_loaded == 1) ? 1 : 0; + /* load required functions; sets dll_load=3 on error */ + load_function("pcap_close", (void**) &p_pcap_close); + load_function("pcap_compile", (void**) &p_pcap_compile); + load_function("pcap_datalink", (void**) &p_pcap_datalink); + load_function("pcap_dispatch", (void**) &p_pcap_dispatch); + load_function("pcap_findalldevs", (void**) &p_pcap_findalldevs); + load_function("pcap_freealldevs", (void**) &p_pcap_freealldevs); + load_function("pcap_freecode", (void**) &p_pcap_freecode); + load_function("pcap_geterr", (void**) &p_pcap_geterr); + load_function("pcap_lookupnet", (void**) &p_pcap_lookupnet); + load_function("pcap_open_live", (void**) &p_pcap_open_live); + load_function("pcap_setmintocopy", (void**) &p_pcap_setmintocopy); + load_function("pcap_getevent", (void**) &p_pcap_getevent); + load_function("pcap_sendpacket", (void**) &p_pcap_sendpacket); + load_function("pcap_setfilter", (void**) &p_pcap_setfilter); + load_function("pcap_lib_version", (void**) &p_pcap_lib_version); + + if (dll_loaded == 1) { + /* log successful load */ + char* version = p_pcap_lib_version(); + printf("%s\n", version); + if (sim_log) + fprintf(sim_log, "%s\n", version); + } + break; + default: /* loaded or failed */ + break; + } + return (dll_loaded == 1) ? 1 : 0; } /* define functions with dynamic revectoring */ void pcap_close(pcap_t* a) { - if (load_wpcap() != 0) { - p_pcap_close(a); - } + if (load_wpcap() != 0) { + p_pcap_close(a); + } } int pcap_compile(pcap_t* a, struct bpf_program* b, char* c, int d, bpf_u_int32 e) { - if (load_wpcap() != 0) { - return p_pcap_compile(a, b, c, d, e); - } else { - return 0; - } + if (load_wpcap() != 0) { + return p_pcap_compile(a, b, c, d, e); + } else { + return 0; + } } int pcap_datalink(pcap_t* a) { - if (load_wpcap() != 0) { - return p_pcap_datalink(a); - } else { - return 0; - } + if (load_wpcap() != 0) { + return p_pcap_datalink(a); + } else { + return 0; + } } int pcap_dispatch(pcap_t* a, int b, pcap_handler c, u_char* d) { - if (load_wpcap() != 0) { - return p_pcap_dispatch(a, b, c, d); - } else { - return 0; - } + if (load_wpcap() != 0) { + return p_pcap_dispatch(a, b, c, d); + } else { + return 0; + } } int pcap_findalldevs(pcap_if_t** a, char* b) { - if (load_wpcap() != 0) { - return p_pcap_findalldevs(a, b); - } else { - *a = 0; - strcpy(b, no_wpcap); - return -1; - } + if (load_wpcap() != 0) { + return p_pcap_findalldevs(a, b); + } else { + *a = 0; + strcpy(b, no_wpcap); + return -1; + } } void pcap_freealldevs(pcap_if_t* a) { - if (load_wpcap() != 0) { - p_pcap_freealldevs(a); - } + if (load_wpcap() != 0) { + p_pcap_freealldevs(a); + } } void pcap_freecode(struct bpf_program* a) { - if (load_wpcap() != 0) { - p_pcap_freecode(a); - } + if (load_wpcap() != 0) { + p_pcap_freecode(a); + } } char* pcap_geterr(pcap_t* a) { - if (load_wpcap() != 0) { - return p_pcap_geterr(a); - } else { - return (char*) 0; - } + if (load_wpcap() != 0) { + return p_pcap_geterr(a); + } else { + return (char*) 0; + } } int pcap_lookupnet(const char* a, bpf_u_int32* b, bpf_u_int32* c, char* d) { - if (load_wpcap() != 0) { - return p_pcap_lookupnet(a, b, c, d); - } else { - return 0; - } + if (load_wpcap() != 0) { + return p_pcap_lookupnet(a, b, c, d); + } else { + return 0; + } } pcap_t* pcap_open_live(const char* a, int b, int c, int d, char* e) { - if (load_wpcap() != 0) { - return p_pcap_open_live(a, b, c, d, e); - } else { - return (pcap_t*) 0; - } + if (load_wpcap() != 0) { + return p_pcap_open_live(a, b, c, d, e); + } else { + return (pcap_t*) 0; + } +} + +int pcap_setmintocopy(pcap_t* a, int b) { + if (load_wpcap() != 0) { + return p_pcap_setmintocopy(a, b); + } else { + return 0; + } +} + +HANDLE pcap_getevent(pcap_t* a) { + if (load_wpcap() != 0) { + return p_pcap_getevent(a); + } else { + return (HANDLE) 0; + } } int pcap_sendpacket(pcap_t* a, const u_char* b, int c) { - if (load_wpcap() != 0) { - return p_pcap_sendpacket(a, b, c); - } else { - return 0; - } + if (load_wpcap() != 0) { + return p_pcap_sendpacket(a, b, c); + } else { + return 0; + } } int pcap_setfilter(pcap_t* a, struct bpf_program* b) { - if (load_wpcap() != 0) { - return p_pcap_setfilter(a, b); - } else { - return 0; - } + if (load_wpcap() != 0) { + return p_pcap_setfilter(a, b); + } else { + return 0; + } } #endif @@ -848,56 +1023,297 @@ int pcap_sendpacket(pcap_t* handle, const u_char* msg, int len) } #endif /* !HAS_PCAP_SENDPACKET */ +#ifdef _WIN32 +#include +#include + +static int pcap_mac_if_win32(char *AdapterName, UCHAR MACAddress[6]) +{ + LPADAPTER lpAdapter; + PPACKET_OID_DATA OidData; + BOOLEAN Status; + int ReturnValue; + HINSTANCE hDll; /* handle to DLL */ + LPADAPTER (*p_PacketOpenAdapter)(PCHAR AdapterName); + VOID (*p_PacketCloseAdapter)(LPADAPTER lpAdapter); + BOOLEAN (*p_PacketRequest)(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData); + + hDll = LoadLibrary(TEXT("packet.dll")); + p_PacketOpenAdapter = (void *)GetProcAddress(hDll, "PacketOpenAdapter"); + p_PacketCloseAdapter = (void *)GetProcAddress(hDll, "PacketCloseAdapter"); + p_PacketRequest = (void *)GetProcAddress(hDll, "PacketRequest"); + + /* Open the selected adapter */ + + lpAdapter = p_PacketOpenAdapter(AdapterName); + + if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE)) { + FreeLibrary(hDll); + return -1; + } + + /* Allocate a buffer to get the MAC adress */ + + OidData = malloc(6 + sizeof(PACKET_OID_DATA)); + if (OidData == NULL) { + p_PacketCloseAdapter(lpAdapter); + FreeLibrary(hDll); + return -1; + } + + /* Retrieve the adapter MAC querying the NIC driver */ + + OidData->Oid = OID_802_3_CURRENT_ADDRESS; + + OidData->Length = 6; + memset(OidData->Data, 0, 6); + + Status = p_PacketRequest(lpAdapter, FALSE, OidData); + if(Status) { + memcpy(MACAddress, OidData->Data, 6); + ReturnValue = 0; + } else + ReturnValue = -1; + + free(OidData); + p_PacketCloseAdapter(lpAdapter); + FreeLibrary(hDll); + return ReturnValue; +} +#endif + +static void eth_get_nic_hw_addr(ETH_DEV* dev, char *devname) +{ + memset(&dev->host_nic_phy_hw_addr, 0, sizeof(dev->host_nic_phy_hw_addr)); + dev->have_host_nic_phy_addr = 0; +#ifdef _WIN32 + if (!pcap_mac_if_win32(devname, (UCHAR *)&dev->host_nic_phy_hw_addr)) + dev->have_host_nic_phy_addr = 1; +#else +{ + char command[1024]; + FILE *f; + + memset(command, 0, sizeof(command)); + snprintf(command, sizeof(command)-1, "ifconfig %s | grep [0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F] >NIC.hwaddr", devname); + system(command); + if (f = fopen("NIC.hwaddr", "r")) { + if (fgets(command, sizeof(command)-1, f)) { + char *p1, *p2; + + p1 = strchr(command, ':'); + while (p1) { + p2 = strchr(p1+1, ':'); + if (p2 == p1+3) { + int mac_bytes[6]; + if (6 == sscanf(p1-2, "%02x:%02x:%02x:%02x:%02x:%02x", &mac_bytes[0], &mac_bytes[1], &mac_bytes[2], &mac_bytes[3], &mac_bytes[4], &mac_bytes[5])) { + dev->host_nic_phy_hw_addr[0] = mac_bytes[0]; + dev->host_nic_phy_hw_addr[1] = mac_bytes[1]; + dev->host_nic_phy_hw_addr[2] = mac_bytes[2]; + dev->host_nic_phy_hw_addr[3] = mac_bytes[3]; + dev->host_nic_phy_hw_addr[4] = mac_bytes[4]; + dev->host_nic_phy_hw_addr[5] = mac_bytes[5]; + dev->have_host_nic_phy_addr = 1; + } + break; + } + p1 = p2; + } + } + fclose(f); + remove("NIC.hwaddr"); + } +} +#endif +} + +/* Forward declarations */ +static void +_eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data); + +static t_stat +_eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine); + #if defined (USE_READER_THREAD) #include -void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data); - static void * _eth_reader(void *arg) { ETH_DEV* volatile dev = (ETH_DEV*)arg; int status; +int sched_policy; +struct sched_param sched_priority; +#if defined (_WIN32) +HANDLE hWait = pcap_getevent ((pcap_t*)dev->handle); +#endif +#if defined (MUST_DO_SELECT) struct timeval timeout; +int sel_ret; +int pcap_fd; - timeout.tv_sec = 0; - timeout.tv_usec = 200*1000; +if (dev->pcap_mode) + pcap_fd = pcap_get_selectable_fd((pcap_t *)dev->handle); +else + pcap_fd = dev->fd_handle; +#endif sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n"); - while (dev->handle) { -#if defined (MUST_DO_SELECT) - int sel_ret; + /* Boost Priority for this I/O thread vs the CPU instruction execution + thread which in general won't be readily yielding the processor when + this thread needs to run */ + pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); + ++sched_priority.sched_priority; + pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); + while ((dev->pcap_mode && dev->handle) || dev->fd_handle) { +#if defined (MUST_DO_SELECT) fd_set setl; FD_ZERO(&setl); - FD_SET(pcap_get_selectable_fd((pcap_t *)dev->handle), &setl); - sel_ret = select(1+pcap_get_selectable_fd((pcap_t *)dev->handle), &setl, NULL, NULL, &timeout); + FD_SET(pcap_fd, &setl); + timeout.tv_sec = 0; + timeout.tv_usec = 250*1000; + sel_ret = select(1+pcap_fd, &setl, NULL, NULL, &timeout); if (sel_ret < 0 && errno != EINTR) break; if (sel_ret > 0) { - /* dispatch read request queue available packets */ - status = pcap_dispatch((pcap_t*)dev->handle, -1, ð_callback, (u_char*)dev); - } +#else /* !MUST_DO_SELECT */ +#if defined (_WIN32) + if (WAIT_OBJECT_0 == WaitForSingleObject (hWait, 250)) { #else - /* dispatch read request queue available packets */ - status = pcap_dispatch((pcap_t*)dev->handle, 1, ð_callback, (u_char*)dev); + if (1) { #endif +#endif /* MUST_DO_SELECT */ + if ((dev->pcap_mode) && (!dev->handle)) + break; + if ((!dev->pcap_mode) && (!dev->fd_handle)) + break; + /* dispatch read request queue available packets */ + if (dev->pcap_mode) + status = pcap_dispatch ((pcap_t*)dev->handle, -1, &_eth_callback, (u_char*)dev); +#ifdef USE_TAP_NETWORK + else { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; + + memset(&header, 0, sizeof(header)); + len = read(dev->fd_handle, buf, sizeof(buf)); + if (len > 0) { + status = 1; + header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } else { + status = 0; + } + } +#endif /* USE_TAP_NETWORK */ + + if ((status > 0) && (dev->asynch_io)) { + int wakeup_needed; + pthread_mutex_lock (&dev->lock); + wakeup_needed = (dev->read_queue.count != 0); + pthread_mutex_unlock (&dev->lock); + if (wakeup_needed) { + sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); + sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); + } + } + } } sim_debug(dev->dbit, dev->dptr, "Reader Thread Exiting\n"); return NULL; } + +static void * +_eth_writer(void *arg) +{ +ETH_DEV* volatile dev = (ETH_DEV*)arg; +struct write_request *request; +int sched_policy; +struct sched_param sched_priority; + + /* Boost Priority for this I/O thread vs the CPU instruction execution + thread which in general won't be readily yielding the processor when + this thread needs to run */ + pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); + ++sched_priority.sched_priority; + pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); + + sim_debug(dev->dbit, dev->dptr, "Writer Thread Starting\n"); + + pthread_mutex_lock (&dev->writer_lock); + while ((dev->pcap_mode && dev->handle) || dev->fd_handle) { + pthread_cond_wait (&dev->writer_cond, &dev->writer_lock); + while (request = dev->write_requests) { + /* Pull buffer off request list */ + dev->write_requests = request->next; + pthread_mutex_unlock (&dev->writer_lock); + + dev->write_status = _eth_write(dev, &request->packet, NULL); + + pthread_mutex_lock (&dev->writer_lock); + /* Put buffer on free buffer list */ + request->next = dev->write_buffers; + dev->write_buffers = request; + } + } + pthread_mutex_unlock (&dev->writer_lock); + + sim_debug(dev->dbit, dev->dptr, "Writer Thread Exiting\n"); + return NULL; +} #endif +t_stat eth_set_async (ETH_DEV *dev, int latency) +{ +#if !defined(USE_READER_THREAD) || !defined(SIM_ASYNCH_IO) + char *msg = "Eth: can't operate asynchronously, must poll\r\n"; + printf ("%s", msg); + if (sim_log) fprintf (sim_log, "%s", msg); + return SCPE_NOFNC; +#else + int wakeup_needed; + + dev->asynch_io = 1; + dev->asynch_io_latency = latency; + pthread_mutex_lock (&dev->lock); + wakeup_needed = (dev->read_queue.count != 0); + pthread_mutex_unlock (&dev->lock); + if (wakeup_needed) { + sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); + sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); + } +#endif + return SCPE_OK; +} + +t_stat eth_clr_async (ETH_DEV *dev) +{ +#if !defined(USE_READER_THREAD) || !defined(SIM_ASYNCH_IO) + return SCPE_NOFNC; +#else + /* make sure device exists */ + if (!dev) return SCPE_UNATT; + + dev->asynch_io = 0; + return SCPE_OK; +#endif +} + t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) { - const int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ; + int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ; char errbuf[PCAP_ERRBUF_SIZE]; char temp[1024]; char* savname = name; int num; char* msg; + if (bufsz < ETH_MAX_JUMBO_FRAME) + bufsz = ETH_MAX_JUMBO_FRAME; /* Enable handling of jumbo frames */ + /* initialize device */ eth_zero(dev); @@ -919,23 +1335,87 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) /* probably is not ethX and has no description */ savname = eth_getname_byname(name, temp); if (savname == 0) /* didn't translate */ - return SCPE_OPENERR; + savname = name; } } /* attempt to connect device */ memset(errbuf, 0, sizeof(errbuf)); - dev->handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf); - if (!dev->handle) { /* can't open device */ - msg = "Eth: pcap_open_live error - %s\r\n"; + if (strncmp("tap:", savname, 4)) { + dev->handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf); + if (!dev->handle) { /* can't open device */ + msg = "Eth: pcap_open_live error - %s\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } + dev->pcap_mode = 1; + } else { + int tun = -1; /* TUN/TAP Socket */ + int on = 1; + char dev_name[64] = ""; + +#if defined(USE_TAP_NETWORK) + if (!strcmp(savname, "tap:tapN")) { + msg = "Eth: Must specify actual tap device name (i.e. tap:tap0)\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } +#endif +#if defined(__linux) && defined(USE_TAP_NETWORK) + if ((tun = open("/dev/net/tun", O_RDWR)) >= 0) { + struct ifreq ifr; /* Interface Requests */ + + memset(&ifr, 0, sizeof(ifr)); + /* Set up interface flags */ + strcpy(ifr.ifr_name, savname+4); + ifr.ifr_flags = IFF_TAP|IFF_NO_PI; + + // Send interface requests to TUN/TAP driver. + if (ioctl(tun, TUNSETIFF, &ifr) >= 0) { + if (ioctl(tun, FIONBIO, &on)) { + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + close(tun); + } else { + dev->fd_handle = tun; + strcpy(savname, ifr.ifr_name); + } + } else + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + } else + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); +#elif defined(USE_BSDTUNTAP) && defined(USE_TAP_NETWORK) + snprintf(dev_name, sizeof(dev_name)-1, "/dev/%s", savname+4); + dev_name[sizeof(dev_name)-1] = '\0'; + + if ((tun = open(dev_name, O_RDWR)) >= 0) { + if (ioctl(tun, FIONBIO, &on)) { + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + close(tun); + } else { + dev->fd_handle = tun; + strcpy(savname, savname+4); + } + } else { + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + } +#else + strncpy(errbuf, "No support for tap: devices", sizeof(errbuf)-1); +#endif /* !defined(__linux) && !defined(USE_BSDTUNTAP) */ + } + if (errbuf[0]) { + msg = "Eth: open error - %s\r\n"; printf (msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); return SCPE_OPENERR; - } else { - msg = "Eth: opened %s\r\n"; - printf (msg, savname); - if (sim_log) fprintf (sim_log, msg, savname); } + msg = "Eth: opened OS device %s\r\n"; + printf (msg, savname); + if (sim_log) fprintf (sim_log, msg, savname); + + /* get the NIC's hardware MAC address */ + eth_get_nic_hw_addr(dev, savname); /* save name of device */ dev->name = malloc(strlen(savname)+1); @@ -948,7 +1428,7 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) #if !defined(HAS_PCAP_SENDPACKET) && defined (xBSD) && !defined (__APPLE__) /* Tell the kernel that the header is fully-formed when it gets it. This is required in order to fake the src address. */ - { + if (dev->pcap_mode) { int one = 1; ioctl(pcap_fileno(dev->handle), BIOCSHDRCMPLT, &one); } @@ -958,23 +1438,40 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) { pthread_attr_t attr; +#if defined(_WIN32) + pcap_setmintocopy (dev->handle, 0); +#endif ethq_init (&dev->read_queue, 200); /* initialize FIFO queue */ pthread_mutex_init (&dev->lock, NULL); + pthread_mutex_init (&dev->writer_lock, NULL); + pthread_cond_init (&dev->writer_cond, NULL); pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); pthread_create (&dev->reader_thread, &attr, _eth_reader, (void *)dev); + pthread_create (&dev->writer_thread, &attr, _eth_writer, (void *)dev); pthread_attr_destroy(&attr); } #else /* !defined (USE_READER_THREAD */ #ifdef USE_SETNONBLOCK /* set ethernet device non-blocking so pcap_dispatch() doesn't hang */ - if (pcap_setnonblock (dev->handle, 1, errbuf) == -1) { + if (dev->pcap_mode && (pcap_setnonblock (dev->handle, 1, errbuf) == -1)) { msg = "Eth: Failed to set non-blocking: %s\r\n"; printf (msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); } #endif #endif /* !defined (USE_READER_THREAD */ +#if defined (__APPLE__) + if (1) { + /* Deliver packets immediately, needed for OS X 10.6.2 and later + * (Snow-Leopard). + * See this thread on libpcap and Mac Os X 10.6 Snow Leopard on + * the tcpdump mailinglist: http://seclists.org/tcpdump/2010/q1/110 + */ + int v = 1; + ioctl(pcap_fileno(dev->handle), BIOCIMMEDIATE, &v); + } +#endif return SCPE_OK; } @@ -982,6 +1479,7 @@ t_stat eth_close(ETH_DEV* dev) { char* msg = "Eth: closed %s\r\n"; pcap_t *pcap; + int pcap_fd = dev->fd_handle; /* make sure device exists */ if (!dev) return SCPE_UNATT; @@ -989,14 +1487,40 @@ t_stat eth_close(ETH_DEV* dev) /* close the device */ pcap = (pcap_t *)dev->handle; dev->handle = NULL; - pcap_close(pcap); - printf (msg, dev->name); - if (sim_log) fprintf (sim_log, msg, dev->name); + dev->fd_handle = 0; + dev->have_host_nic_phy_addr = 0; #if defined (USE_READER_THREAD) pthread_join (dev->reader_thread, NULL); + pthread_mutex_destroy (&dev->lock); + pthread_cond_signal (&dev->writer_cond); + pthread_join (dev->writer_thread, NULL); + pthread_mutex_destroy (&dev->writer_lock); + pthread_cond_destroy (&dev->writer_cond); + if (1) { + struct write_request *buffer; + + while (buffer = dev->write_buffers) { + dev->write_buffers = buffer->next; + free(buffer); + } + while (buffer = dev->write_requests) { + dev->write_requests = buffer->next; + free(buffer); + } + } + ethq_destroy (&dev->read_queue); /* release FIFO queue */ #endif + if (dev->pcap_mode) + pcap_close(pcap); +#ifdef USE_TAP_NETWORK + else + close(pcap_fd); +#endif + printf (msg, dev->name); + if (sim_log) fprintf (sim_log, msg, dev->name); + /* clean up the mess */ free(dev->name); eth_zero(dev); @@ -1004,27 +1528,30 @@ t_stat eth_close(ETH_DEV* dev) return SCPE_OK; } -t_stat eth_reflect(ETH_DEV* dev, ETH_MAC mac) +t_stat eth_check_address_conflict (ETH_DEV* dev, + ETH_MAC* const mac) { ETH_PACK send, recv; t_stat status; - int i; - struct timeval delay; + int responses = 0; - /* build a packet */ + sim_debug(dev->dbit, dev->dptr, "Determining Address Conflict...\n"); + + /* build a loopback forward request packet */ memset (&send, 0, sizeof(ETH_PACK)); send.len = ETH_MIN_PACKET; /* minimum packet size */ memcpy(&send.msg[0], mac, sizeof(ETH_MAC)); /* target address */ memcpy(&send.msg[6], mac, sizeof(ETH_MAC)); /* source address */ send.msg[12] = 0x90; /* loopback packet type */ - for (i=14; ireflections = 0; eth_filter(dev, 1, (ETH_MAC *)mac, 0, 0); /* send the packet */ - status = eth_write (dev, &send, NULL); + status = _eth_write (dev, &send, NULL); if (status != SCPE_OK) { char *msg; msg = "Eth: Error Transmitting packet: %s\r\n" @@ -1035,24 +1562,38 @@ t_stat eth_reflect(ETH_DEV* dev, ETH_MAC mac) return status; } - /* if/when we have a sim_os_msleep() we'll use it here instead of this select() */ - delay.tv_sec = 0; - delay.tv_usec = 50*1000; - select(0, NULL, NULL, NULL, &delay); /* make sure things settle into the read path */ + sim_os_ms_sleep (300); /* time for a conflicting host to respond */ - /* empty the read queue and count the reflections */ + /* empty the read queue and count the responses */ do { memset (&recv, 0, sizeof(ETH_PACK)); status = eth_read (dev, &recv, NULL); - if (memcmp(send.msg, recv.msg, ETH_MIN_PACKET)== 0) - dev->reflections++; + if (memcmp(send.msg, recv.msg, 14)== 0) + responses++; } while (recv.len > 0); + sim_debug(dev->dbit, dev->dptr, "Address Conflict = %d\n", responses); + return responses; +} + +t_stat eth_reflect(ETH_DEV* dev) +{ + /* Test with an address no NIC should have. */ + /* We do this to avoid reflections from the wire, */ + /* in the event that a simulated NIC has a MAC address conflict. */ + ETH_MAC mac = {0xfe,0xff,0xff,0xff,0xff,0xfe}; + + sim_debug(dev->dbit, dev->dptr, "Determining Reflections...\n"); + + dev->reflections = 0; + dev->reflections = eth_check_address_conflict (dev, &mac); + sim_debug(dev->dbit, dev->dptr, "Reflections = %d\n", dev->reflections); return dev->reflections; } -t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) +static +t_stat _eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { int status = 1; /* default to failure */ @@ -1067,11 +1608,18 @@ t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) eth_packet_trace (dev, packet->msg, packet->len, "writing"); /* dispatch write request (synchronous; no need to save write info to dev) */ - status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len); + if (dev->pcap_mode) + status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len); +#ifdef USE_TAP_NETWORK + else + status = ((packet->len == write(dev->fd_handle, (void *)packet->msg, packet->len)) ? 0 : -1); +#endif - /* detect sending of decnet loopback packet */ - if ((status == 0) && DECNET_SELF_FRAME(dev->decnet_addr, packet->msg)) - dev->decnet_self_sent += dev->reflections; + /* detect sending of loopback packet */ + if ((status == 0) && (LOOPBACK_SELF_FRAME(dev->physical_addr, packet->msg))) { + dev->loopback_self_sent += dev->reflections; + dev->loopback_self_sent_total++; + } } /* if packet->len */ @@ -1082,19 +1630,480 @@ t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) return ((status == 0) ? SCPE_OK : SCPE_IOERR); } -void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data) +t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) +{ +#ifdef USE_READER_THREAD + struct write_request *request; + int write_queue_size = 1; + + /* make sure device exists */ + if (!dev) return SCPE_UNATT; + + /* Get a buffer */ + pthread_mutex_lock (&dev->writer_lock); + if (request = dev->write_buffers) + dev->write_buffers = request->next; + pthread_mutex_unlock (&dev->writer_lock); + if (!request) + request = malloc(sizeof(*request)); + + /* Copy buffer contents */ + request->packet.len = packet->len; + request->packet.used = packet->used; + request->packet.status = packet->status; + request->packet.crc_len = packet->crc_len; + memcpy(request->packet.msg, packet->msg, packet->len); + + /* Insert buffer at the end of the write list (to make sure that */ + /* packets make it to the wire in the order they were presented here) */ + pthread_mutex_lock (&dev->writer_lock); + request->next = NULL; + if (dev->write_requests) { + struct write_request *last_request = dev->write_requests; + + ++write_queue_size; + while (last_request->next) { + last_request = last_request->next; + ++write_queue_size; + } + last_request->next = request; + } else + dev->write_requests = request; + if (write_queue_size > dev->write_queue_peak) + dev->write_queue_peak = write_queue_size; + pthread_mutex_unlock (&dev->writer_lock); + + /* Awaken writer thread to perform actual write */ + pthread_cond_signal (&dev->writer_cond); + + /* Return with a status from some prior write */ + if (routine) + (routine)(dev->write_status); + return dev->write_status; +#else + return _eth_write(dev, packet, routine); +#endif +} + +static int +_eth_hash_lookup(ETH_MULTIHASH hash, const u_char* data) +{ + int key = 0x3f & (eth_crc32(0, data, 6) >> 26); + + key ^= 0x3f; + return (hash[key>>3] & (1 << (key&0x7))); +} + +static int +_eth_hash_validate(ETH_MAC *MultiCastList, int count, ETH_MULTIHASH hash) +{ + ETH_MULTIHASH lhash; + int i; + + memset(lhash, 0, sizeof(lhash)); + for (i=0; i> 26); + + key ^= 0x3F; + printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X Key: %X, Byte: %X, Val: %X\n", + MultiCastList[i][0], MultiCastList[i][1], MultiCastList[i][2], MultiCastList[i][3], MultiCastList[i][4], MultiCastList[i][5], + key, key>>3, (1 << (key&0x7))); + lhash[key>>3] |= (1 << (key&0x7)); + } + if (memcmp(hash, lhash, sizeof(lhash))) { + printf("Inconsistent Computed Hash:\n"); + printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", + hash[0], hash[1], hash[2], hash[3], + hash[4], hash[5], hash[6], hash[7]); + printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", + lhash[0], lhash[1], lhash[2], lhash[3], + lhash[4], lhash[5], lhash[6], lhash[7]); + } + printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", + hash[0], hash[1], hash[2], hash[3], + hash[4], hash[5], hash[6], hash[7]); + printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", + lhash[0], lhash[1], lhash[2], lhash[3], + lhash[4], lhash[5], lhash[6], lhash[7]); + return 0; +} + +static void +_eth_test_multicast_hash() +{ + ETH_MAC tMacs[] = { + {0xAB, 0x00, 0x04, 0x01, 0xAC, 0x10}, + {0xAB, 0x00, 0x00, 0x04, 0x00, 0x00}, + {0x09, 0x00, 0x2B, 0x00, 0x00, 0x0F}, + {0x09, 0x00, 0x2B, 0x02, 0x01, 0x04}, + {0x09, 0x00, 0x2B, 0x02, 0x01, 0x07}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {0x01, 0x00, 0x5E, 0x00, 0x00, 0x01}}; + ETH_MULTIHASH thash = {0x01, 0x40, 0x00, 0x00, 0x48, 0x88, 0x40, 0x00}; + + _eth_hash_validate(tMacs, sizeof(tMacs)/sizeof(tMacs[0]), thash); +} + +/* The IP header */ +struct IPHeader { + uint8 verhlen; /* Version & Header Length in dwords */ +#define IP_HLEN(IP) (((IP)->verhlen&0xF)<<2) /* Header Length in Bytes */ +#define IP_VERSION(IP) ((((IP)->verhlen)>>4)&0xF) /* IP Version */ + uint8 tos; /* Type of service */ + uint16 total_len; /* Length of the packet in dwords */ + uint16 ident; /* unique identifier */ + uint16 flags; /* Fragmentation Flags */ +#define IP_DF_FLAG (0x4000) +#define IP_MF_FLAG (0x2000) +#define IP_OFFSET_MASK (0x1FFF) +#define IP_FRAG_DF(IP) (ntohs(((IP)->flags))&IP_DF_FLAG) +#define IP_FRAG_MF(IP) (ntohs(((IP)->flags))&IP_MF_FLAG) +#define IP_FRAG_OFFSET(IP) (ntohs(((IP)->flags))&IP_OFFSET_MASK) + uint8 ttl; /* Time to live */ + uint8 proto; /* Protocol number (TCP, UDP etc) */ + uint16 checksum; /* IP checksum */ + uint32 source_ip; /* Source Address */ + uint32 dest_ip; /* Destination Address */ +}; + +/* ICMP header */ +struct ICMPHeader { + uint8 type; /* ICMP packet type */ + uint8 code; /* Type sub code */ + uint16 checksum; /* ICMP Checksum */ + uint32 otherstuff[1];/* optional data */ +}; + +struct UDPHeader { + uint16 source_port; + uint16 dest_port; + uint16 length; /* The length of the entire UDP datagram, including both header and Data fields. */ + uint16 checksum; +}; + +struct TCPHeader { + uint16 source_port; + uint16 dest_port; + uint32 sequence_number; + uint32 acknowledgement_number; + uint16 data_offset_and_flags; +#define TCP_DATA_OFFSET(TCP) ((ntohs((TCP)->data_offset_and_flags)>>12)<<2) +#define TCP_CWR_FLAG (0x80) +#define TCP_ECR_FLAG (0x40) +#define TCP_URG_FLAG (0x20) +#define TCP_ACK_FLAG (0x10) +#define TCP_PSH_FLAG (0x08) +#define TCP_RST_FLAG (0x04) +#define TCP_SYN_FLAG (0x02) +#define TCP_FIN_FLAG (0x01) +#define TCP_FLAGS_MASK (0xFF) + uint16 window; + uint16 checksum; + uint16 urgent; + uint16 otherstuff[1]; /* The rest of the packet */ +}; + +#ifndef IPPROTO_TCP +#define IPPROTO_TCP 6 /* tcp */ +#endif +#ifndef IPPROTO_UDP +#define IPPROTO_UDP 17 /* user datagram protocol */ +#endif +#ifndef IPPROTO_ICMP +#define IPPROTO_ICMP 1 /* control message protocol */ +#endif + +static uint16 +ip_checksum(uint16 *buffer, int size) +{ + unsigned long cksum = 0; + + /* Sum all the words together, adding the final byte if size is odd */ + while (size > 1) { + cksum += *buffer++; + size -= sizeof(*buffer); + } + if (size) { + uint8 endbytes[2]; + + endbytes[0] = *((uint8 *)buffer); + endbytes[1] = 0; + cksum += *((uint16 *)endbytes); + } + + /* Do a little shuffling */ + cksum = (cksum >> 16) + (cksum & 0xffff); + cksum += (cksum >> 16); + + /* Return the bitwise complement of the resulting mishmash */ + return (uint16)(~cksum); +} + +static uint16 +pseudo_checksum(uint16 len, uint16 proto, uint16 *src_addr, uint16 *dest_addr, uint8 *buff) +{ + uint32 sum; + + /* Sum the data first */ + sum = 0xffff&(~ip_checksum((uint16 *)buff, len)); + + /* add the pseudo header which contains the IP source and destinationn addresses */ + sum += src_addr[0]; + sum += src_addr[1]; + sum += dest_addr[0]; + sum += dest_addr[1]; + /* and the protocol number and the length of the UDP packet */ + sum = sum + htons(proto) + htons(len); + + /* Do a little shuffling */ + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + + /* Return the bitwise complement of the resulting mishmash */ + return (uint16)(~sum); +} + +static void +_eth_fix_ip_jumbo_offload(ETH_DEV* dev, const u_char* msg, int len) +{ + unsigned short* proto = (unsigned short*) &msg[12]; + struct IPHeader *IP; + struct TCPHeader *TCP = NULL; + struct UDPHeader *UDP; + struct ICMPHeader *ICMP; + uint16 orig_checksum; + uint16 payload_len; + uint16 mtu_payload; + uint16 ip_flags; + uint16 frag_offset; + struct pcap_pkthdr header; + uint16 tcp_flags; + + /* Only interested in IP frames */ + if (ntohs(*proto) != 0x0800) { + ++dev->jumbo_dropped; /* Non IP Frames are dropped */ + return; + } + IP = (struct IPHeader *)&msg[14]; + if (IP_VERSION(IP) != 4) { + ++dev->jumbo_dropped; /* Non IPv4 jumbo frames are dropped */ + return; + } + if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) { + ++dev->jumbo_dropped; /* Bogus header length frames are dropped */ + return; + } + if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) { + ++dev->jumbo_dropped; /* Previously fragmented, but currently jumbo sized frames are dropped */ + return; + } + switch (IP->proto) { + case IPPROTO_UDP: + UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); + if (ntohs(UDP->length) > (len-IP_HLEN(IP))) { + ++dev->jumbo_dropped; /* Bogus UDP packet length (packet contained length exceeds packet size) frames are dropped */ + return; + } + if (UDP->checksum == 0) + break; /* UDP Cghecksums are disabled */ + orig_checksum = UDP->checksum; + UDP->checksum = 0; + UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); + if (orig_checksum != UDP->checksum) + eth_packet_trace (dev, msg, len, "reading jumbo UDP header Checksum Fixed"); + break; + case IPPROTO_ICMP: + ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_checksum = ICMP->checksum; + ICMP->checksum = 0; + ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); + if (orig_checksum != ICMP->checksum) + eth_packet_trace (dev, msg, len, "reading jumbo ICMP header Checksum Fixed"); + break; + case IPPROTO_TCP: + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + if ((TCP_DATA_OFFSET(TCP) > (len-IP_HLEN(IP))) || (TCP_DATA_OFFSET(TCP) < 20)) { + ++dev->jumbo_dropped; /* Bogus TCP packet header length (packet contained length exceeds packet size) frames are dropped */ + return; + } + /* We don't do anything with the TCP checksum since we're going to resegment the TCP data below */ + break; + default: + ++dev->jumbo_dropped; /* We onlt handle UDP, ICMP and TCP jumbo frames others are dropped */ + return; + } + /* Reasonable Checksums are now in the jumbo packet, but we've got to actually */ + /* deliver ONLY standard sized ethernet frames. Our job here is to now act as */ + /* a router might have to and fragment these IPv4 frames as they are delivered */ + /* into the virtual NIC. We do this by walking down the packet and dispatching */ + /* a chunk at a time recomputing an appropriate header for each chunk. For */ + /* datagram oriented protocols (UDP and ICMP) this is done by simple packet */ + /* fragmentation. For TCP this is done by breaking large packets into separate */ + /* TCP packets. */ + memset(&header, 0, sizeof(header)); + switch (IP->proto) { + case IPPROTO_UDP: + case IPPROTO_ICMP: + ++dev->jumbo_fragmented; + payload_len = ntohs(IP->total_len) - IP_HLEN(IP); + mtu_payload = ETH_MIN_JUMBO_FRAME - 14 - IP_HLEN(IP); + frag_offset = 0; + while (payload_len > 0) { + ip_flags = frag_offset; + if (payload_len > mtu_payload) { + ip_flags |= IP_MF_FLAG; + IP->total_len = htons(((mtu_payload>>3)<<3) + IP_HLEN(IP)); + } else { + IP->total_len = htons(payload_len + IP_HLEN(IP)); + } + IP->flags = htons(ip_flags); + IP->checksum = 0; + IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); + header.len = 14 + ntohs(IP->total_len); + eth_packet_trace (dev, ((u_char *)IP)-14, header.len, "reading Datagram fragment"); +#if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET + { /* Debugging is easier it we read packets directly with pcap */ + ETH_PACK pkt; + + memset(&pkt, 0, sizeof(pkt)); + memcpy(pkt.msg, ((u_char *)IP)-14, header.len); + pkt.len = header.len; + _eth_write(dev, &pkt, NULL); + } +#else + _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); +#endif + payload_len -= (ntohs(IP->total_len) - IP_HLEN(IP)); + frag_offset += (ntohs(IP->total_len) - IP_HLEN(IP))>>3; + if (payload_len > 0) { + /* Move the MAC and IP headers down to just prior to the next payload segment */ + memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP)); + IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - IP_HLEN(IP)); + } + } + break; + case IPPROTO_TCP: + ++dev->jumbo_fragmented; + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + tcp_flags = ntohs(TCP->data_offset_and_flags)&TCP_FLAGS_MASK; + TCP->data_offset_and_flags = htons(((TCP_DATA_OFFSET(TCP)>>2)<<12)|TCP_ACK_FLAG); + payload_len = ntohs(IP->total_len) - IP_HLEN(IP) - TCP_DATA_OFFSET(TCP); + mtu_payload = ETH_MIN_JUMBO_FRAME - 14 - IP_HLEN(IP) - TCP_DATA_OFFSET(TCP); + while (payload_len > 0) { + if (payload_len > mtu_payload) { + IP->total_len = htons(mtu_payload + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + } else { + TCP->data_offset_and_flags = htons(ntohs(TCP->data_offset_and_flags)|(tcp_flags&(TCP_PSH_FLAG|TCP_ACK_FLAG|TCP_FIN_FLAG|TCP_RST_FLAG))); + IP->total_len = htons(payload_len + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + } + IP->checksum = 0; + IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); + TCP->checksum = 0; + TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); + header.len = 14 + ntohs(IP->total_len); + eth_packet_trace_ex (dev, ((u_char *)IP)-14, header.len, "reading TCP segment", 1, dev->dbit); +#if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET + { /* Debugging is easier it we read packets directly with pcap */ + ETH_PACK pkt; + + memset(&pkt, 0, sizeof(pkt)); + memcpy(pkt.msg, ((u_char *)IP)-14, header.len); + pkt.len = header.len; + _eth_write(dev, &pkt, NULL); + } +#else + _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); +#endif + payload_len -= (ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); + if (payload_len > 0) { + /* Move the MAC, IP and TCP headers down to just prior to the next payload segment */ + memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + TCP->sequence_number = htonl(mtu_payload + ntohl(TCP->sequence_number)); + } + } + break; + } +} + +static void +_eth_fix_ip_xsum_offload(ETH_DEV* dev, u_char* msg, int len) +{ + unsigned short* proto = (unsigned short*) &msg[12]; + struct IPHeader *IP; + struct TCPHeader *TCP; + struct UDPHeader *UDP; + struct ICMPHeader *ICMP; + uint16 orig_checksum; + + /* Only need to process locally originated packets */ + if ((!dev->have_host_nic_phy_addr) || (memcmp(msg+6, dev->host_nic_phy_hw_addr, 6))) + return; + /* Only interested in IP frames */ + if (ntohs(*proto) != 0x0800) + return; + IP = (struct IPHeader *)&msg[14]; + if (IP_VERSION(IP) != 4) + return; /* Only interested in IPv4 frames */ + if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) + return; /* Bogus header length */ + orig_checksum = IP->checksum; + IP->checksum = 0; + IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); + if (orig_checksum != IP->checksum) + eth_packet_trace (dev, msg, len, "reading IP header Checksum Fixed"); + if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) + return; /* Insufficient data to compute payload checksum */ + switch (IP->proto) { + case IPPROTO_UDP: + UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); + if (ntohs(UDP->length) > (len-IP_HLEN(IP))) + return; /* packet contained length exceeds packet size */ + if (UDP->checksum == 0) + return; /* UDP Cghecksums are disabled */ + orig_checksum = UDP->checksum; + UDP->checksum = 0; + UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); + if (orig_checksum != UDP->checksum) + eth_packet_trace (dev, msg, len, "reading UDP header Checksum Fixed"); + break; + case IPPROTO_TCP: + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_checksum = TCP->checksum; + TCP->checksum = 0; + TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); + if (orig_checksum != TCP->checksum) + eth_packet_trace (dev, msg, len, "reading TCP header Checksum Fixed"); + break; + case IPPROTO_ICMP: + ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_checksum = ICMP->checksum; + ICMP->checksum = 0; + ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); + if (orig_checksum != ICMP->checksum) + eth_packet_trace (dev, msg, len, "reading ICMP header Checksum Fixed"); + break; + } +} + +static void +_eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data) { ETH_DEV* dev = (ETH_DEV*) info; #ifdef USE_BPF int to_me = 1; + + /* AUTODIN II hash mode? */ + if ((dev->hash_filter) && (data[0] & 0x01) && (!dev->promiscuous) && (!dev->all_multicast)) + to_me = _eth_hash_lookup(dev->hash, data); #else /* !USE_BPF */ int to_me = 0; int from_me = 0; int i; -#ifdef ETH_DEBUG -// eth_packet_trace (dev, data, header->len, "received"); -#endif + eth_packet_trace (dev, data, header->len, "received"); + for (i = 0; i < dev->addr_count; i++) { if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1; if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1; @@ -1105,13 +2114,19 @@ void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* /* promiscuous mode? */ if (dev->promiscuous) to_me = 1; + + /* AUTODIN II hash mode? */ + if ((dev->hash_filter) && (!to_me) && (data[0] & 0x01)) + to_me = _eth_hash_lookup(dev->hash, data); #endif /* USE_BPF */ - /* detect sending of decnet loopback packet */ - if (DECNET_SELF_FRAME(dev->decnet_addr, data)) { - /* lower reflection count - if already zero, pass it on */ - if (dev->decnet_self_sent > 0) { - dev->decnet_self_sent--; + /* detect reception of loopback packet to our physical address */ + if (LOOPBACK_SELF_FRAME(dev->physical_addr, data)) { + dev->loopback_self_rcvd_total++; + /* lower reflection count - if already zero, pass it on */ + if (dev->loopback_self_sent > 0) { + eth_packet_trace (dev, data, header->len, "ignored"); + dev->loopback_self_sent--; to_me = 0; } #ifndef USE_BPF @@ -1125,26 +2140,60 @@ void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* #else /* !USE_BPF */ if (to_me && !from_me) { #endif + if (header->len > ETH_MIN_JUMBO_FRAME) { + _eth_fix_ip_jumbo_offload(dev, data, header->len); + return; + } #if defined (USE_READER_THREAD) - ETH_PACK tmp_packet; + if (1) { + int crc_len = 0; + uint8 crc_data[4]; + uint32 len = header->len; + u_char* moved_data = NULL; - /* set data in passed read packet */ - tmp_packet.len = header->len; - memcpy(tmp_packet.msg, data, header->len); - if (dev->need_crc) - eth_add_crc32(&tmp_packet); + if (header->len < ETH_MIN_PACKET) { /* Pad runt packets before CRC append */ + moved_data = malloc(ETH_MIN_PACKET); + memcpy(moved_data, data, len); + memset(moved_data + len, 0, ETH_MIN_PACKET-len); + len = ETH_MIN_PACKET; + data = moved_data; + } - eth_packet_trace (dev, tmp_packet.msg, tmp_packet.len, "rcvqd"); + /* If necessary, fix IP header checksums for packets originated locally */ + /* but were presumed to be traversing a NIC which was going to handle that task */ + /* This must be done before any needed CRC calculation */ + _eth_fix_ip_xsum_offload(dev, (u_char*)data, len); + + if (dev->need_crc) + crc_len = eth_get_packet_crc32_data(data, len, crc_data); - pthread_mutex_lock (&dev->lock); - ethq_insert(&dev->read_queue, 2, &tmp_packet, 0); - pthread_mutex_unlock (&dev->lock); + eth_packet_trace (dev, data, len, "rcvqd"); + + pthread_mutex_lock (&dev->lock); + ethq_insert_data(&dev->read_queue, 2, data, 0, len, crc_len, crc_data, 0); + pthread_mutex_unlock (&dev->lock); + free(moved_data); + } #else /* set data in passed read packet */ dev->read_packet->len = header->len; memcpy(dev->read_packet->msg, data, header->len); + /* Handle runt case and pad with zeros. */ + /* The real NIC won't hand us runts from the wire, BUT we may be getting */ + /* some packets looped back before they actually traverse the wire */ + /* (by an internal bridge device for instance) */ + if (header->len < ETH_MIN_PACKET) { + memset(&dev->read_packet->msg[header->len], 0, ETH_MIN_PACKET-header->len); + dev->read_packet->len = ETH_MIN_PACKET; + } + /* If necessary, fix IP header checksums for packets originated by the local host */ + /* but were presumed to be traversing a NIC which was going to handle that task */ + /* This must be done before any needed CRC calculation */ + _eth_fix_ip_xsum_offload(dev, dev->read_packet->msg, dev->read_packet->len); if (dev->need_crc) - eth_add_crc32(dev->read_packet); + dev->read_packet->crc_len = eth_add_packet_crc32(dev->read_packet->msg, dev->read_packet->len); + else + dev->read_packet->crc_len = 0; eth_packet_trace (dev, dev->read_packet->msg, dev->read_packet->len, "reading"); @@ -1155,28 +2204,46 @@ void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* } } -t_stat eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) +int eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { int status; /* make sure device exists */ - if (!dev) return SCPE_UNATT; + if (!dev) return 0; /* make sure packet exists */ - if (!packet) return SCPE_ARG; + if (!packet) return 0; + packet->len = 0; #if !defined (USE_READER_THREAD) /* set read packet */ dev->read_packet = packet; - packet->len = 0; /* set optional callback routine */ dev->read_callback = routine; /* dispatch read request to either receive a filtered packet or timeout */ do { - status = pcap_dispatch((pcap_t*)dev->handle, 1, ð_callback, (u_char*)dev); + if (dev->pcap_mode) + status = pcap_dispatch((pcap_t*)dev->handle, 1, &_eth_callback, (u_char*)dev); +#ifdef USE_TAP_NETWORK + else { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; + + memset(&header, 0, sizeof(header)); + len = read(dev->fd_handle, buf, sizeof(buf)); + if (len > 0) { + status = 1; + header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } else { + status = 0; + } + } +#endif /* USE_TAP_NETWORK */ } while ((status) && (0 == packet->len)); #else /* USE_READER_THREAD */ @@ -1186,23 +2253,34 @@ t_stat eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) if (dev->read_queue.count > 0) { ETH_ITEM* item = &dev->read_queue.item[dev->read_queue.head]; packet->len = item->packet.len; - memcpy(packet->msg, item->packet.msg, packet->len); - if (routine) - routine(status); + packet->crc_len = item->packet.crc_len; + memcpy(packet->msg, item->packet.msg, ((packet->len > packet->crc_len) ? packet->len : packet->crc_len)); + status = 1; ethq_remove(&dev->read_queue); } pthread_mutex_unlock (&dev->lock); + if ((status) && (routine)) + routine(0); #endif - return SCPE_OK; + return status; } -t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, +t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous) +{ +return eth_filter_hash(dev, addr_count, addresses, + all_multicast, promiscuous, + NULL); +} + +t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, + ETH_BOOL all_multicast, ETH_BOOL promiscuous, + ETH_MULTIHASH* const hash) { int i; bpf_u_int32 bpf_subnet, bpf_netmask; - char buf[110+66*ETH_FILTER_MAX]; + char buf[114+66*ETH_FILTER_MAX]; char errbuf[PCAP_ERRBUF_SIZE]; char mac[20]; char* buf2; @@ -1221,6 +2299,11 @@ t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, else if (!addresses) return SCPE_ARG; + /* test reflections. This is done early in this routine since eth_reflect */ + /* calls eth_filter recursively and thus changes the state of the device. */ + if (dev->reflections == -1) + status = eth_reflect(dev); + /* set new filter addresses */ for (i = 0; i < addr_count; i++) memcpy(dev->filter_address[i], addresses[i], sizeof(ETH_MAC)); @@ -1230,6 +2313,15 @@ t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, dev->all_multicast = all_multicast; dev->promiscuous = promiscuous; + /* store multicast hash data */ + dev->hash_filter = (hash != NULL); + if (hash) { + memcpy(dev->hash, hash, sizeof(*hash)); + sim_debug(dev->dbit, dev->dptr, "Multicast Hash: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n", + dev->hash[0], dev->hash[1], dev->hash[2], dev->hash[3], + dev->hash[4], dev->hash[5], dev->hash[6], dev->hash[7]); + } + /* print out filter information if debugging */ if (dev->dptr->dctrl & dev->dbit) { sim_debug(dev->dbit, dev->dptr, "Filter Set\n"); @@ -1244,10 +2336,6 @@ t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, sim_debug(dev->dbit, dev->dptr, "Promiscuous\n"); } - /* test reflections */ - if (dev->reflections == -1) - status = eth_reflect(dev, dev->filter_address[0]); - /* setup BPF filters and other fields to minimize packet delivery */ strcpy(buf, ""); @@ -1258,10 +2346,12 @@ t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, for (i = 0; i < addr_count; i++) { eth_mac_fmt(&dev->filter_address[i], mac); if (!strstr(buf, mac)) /* eliminate duplicates */ - sprintf(&buf[strlen(buf)], "%s(ether dst %s)", (*buf) ? " or " : "", mac); + sprintf(&buf[strlen(buf)], "%s(ether dst %s)", (*buf) ? " or " : "((", mac); } - if (dev->all_multicast) - sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : ""); + if (dev->all_multicast || dev->hash_filter) + sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : "(("); + if (strlen(buf) > 0) + sprintf(&buf[strlen(buf)], ")"); } /* construct source filters - this prevents packets from being reflected back @@ -1281,66 +2371,84 @@ t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, } sprintf (&buf[strlen(buf)], ")"); } - /* When starting, DECnet sends out a packet with the source and destination - addresses set to the same value as the DECnet MAC address. This packet is - designed to find and help diagnose DECnet address conflicts. Normally, this - packet would not be seen by the sender, only by the other machine that has - the same DECnet address. If the ethernet subsystem is reflecting packets, - DECnet will fail to start if it sees the reflected packet, since it thinks - another system is using this DECnet address. We have to let these packets - through, so that if another machine has the same DECnet address that we - can detect it. Both eth_write() and eth_callback() help by checking the - reflection count - eth_write() adds the reflection count to - dev->decnet_self_sent, and eth_callback() check the value - if the - dev->decnet_self_sent count is zero, then the packet has come from another - machine with the same address, and needs to be passed on to the simulated - machine. */ - memset(dev->decnet_addr, 0, sizeof(ETH_MAC)); - /* check for decnet address in filters */ + if (strlen(buf) > 0) + sprintf(&buf[strlen(buf)], ")"); + /* When changing the Physical Address on a LAN interface, VMS sends out a + loopback packet with the source and destination addresses set to the same + value as the Physical Address which is being setup. This packet is + designed to find and help diagnose MAC address conflicts (which also + include DECnet address conflicts). Normally, this packet would not be + seen by the sender, only by the other machine that has the same Physical + Address (or possibly DECnet address). If the ethernet subsystem is + reflecting packets, the network startup will fail to start if it sees the + reflected packet, since it thinks another system is using this Physical + Address (or DECnet address). We have to let these packets through, so + that if another machine has the same Physical Address (or DECnet address) + that we can detect it. Both eth_write() and _eth_callback() help by + checking the reflection count - eth_write() adds the reflection count to + dev->loopback_self_sent, and _eth_callback() check the value - if the + dev->loopback_self_sent count is zero, then the packet has come from + another machine with the same address, and needs to be passed on to the + simulated machine. */ + memset(dev->physical_addr, 0, sizeof(ETH_MAC)); + dev->loopback_self_sent = 0; + /* check for physical address in filters */ if ((addr_count) && (dev->reflections > 0)) { for (i = 0; i < addr_count; i++) { + if (dev->filter_address[i][0]&1) + continue; /* skip all multicast addresses */ eth_mac_fmt(&dev->filter_address[i], mac); - if (memcmp(mac, "AA:00:04", 8) == 0) { - memcpy(dev->decnet_addr, &dev->filter_address[i], sizeof(ETH_MAC)); - /* let packets through where dst and src are the same as our decnet address */ + if (strcmp(mac, "00:00:00:00:00:00") != 0) { + memcpy(dev->physical_addr, &dev->filter_address[i], sizeof(ETH_MAC)); + /* let packets through where dst and src are the same as our physical address */ sprintf (&buf[strlen(buf)], " or ((ether dst %s) and (ether src %s))", mac, mac); break; } } } + if ((0 == strlen(buf)) && (!dev->promiscuous)) /* Empty filter means match nothing */ + strcpy(buf, "ether host fe:ff:ff:ff:ff:ff"); /* this should be a good match nothing filter */ sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf); - /* get netmask, which is required for compiling */ - if (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0) { + if (dev->pcap_mode && (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0)) { bpf_netmask = 0; } #ifdef USE_BPF - /* compile filter string */ - if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { - sprintf(errbuf, "%s", pcap_geterr(dev->handle)); - msg = "Eth: pcap_compile error: %s\r\n"; - printf(msg, errbuf); - if (sim_log) fprintf (sim_log, msg, errbuf); - /* show erroneous BPF string */ - msg = "Eth: BPF string is: |%s|\r\n"; - printf (msg, buf); - if (sim_log) fprintf (sim_log, msg, buf); - } else { - /* apply compiled filter string */ - if ((status = pcap_setfilter(dev->handle, &bpf)) < 0) { + if (dev->pcap_mode) { +#ifdef USE_READER_THREAD + pthread_mutex_lock (&dev->lock); + ethq_clear (&dev->read_queue); /* Empty FIFO Queue when filter list changes */ + pthread_mutex_unlock (&dev->lock); +#endif + /* compile filter string */ + if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { sprintf(errbuf, "%s", pcap_geterr(dev->handle)); - msg = "Eth: pcap_setfilter error: %s\r\n"; + msg = "Eth: pcap_compile error: %s\r\n"; printf(msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); + sim_debug(dev->dbit, dev->dptr, "Eth: pcap_compile error: %s\n", errbuf); + /* show erroneous BPF string */ + msg = "Eth: BPF string is: |%s|\r\n"; + printf (msg, buf); + if (sim_log) fprintf (sim_log, msg, buf); } else { + /* apply compiled filter string */ + if ((status = pcap_setfilter(dev->handle, &bpf)) < 0) { + sprintf(errbuf, "%s", pcap_geterr(dev->handle)); + msg = "Eth: pcap_setfilter error: %s\r\n"; + printf(msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + sim_debug(dev->dbit, dev->dptr, "Eth: pcap_setfilter error: %s\n", errbuf); + } else { #ifdef USE_SETNONBLOCK - /* set file non-blocking */ - status = pcap_setnonblock (dev->handle, 1, errbuf); + /* set file non-blocking */ + status = pcap_setnonblock (dev->handle, 1, errbuf); #endif /* USE_SETNONBLOCK */ + } + pcap_freecode(&bpf); } - pcap_freecode(&bpf); } #endif /* USE_BPF */ @@ -1387,46 +2495,59 @@ int eth_host_devices(int used, int max, ETH_LIST* list) #if defined(_WIN32) /* replace device description with user-defined adapter name (if defined) */ for (i=0; i sizeof(regval))) { - RegCloseKey (reghnd); - continue; - } + if((regtype != REG_SZ) || (reglen > sizeof(regval))) { + RegCloseKey (reghnd); + continue; + } /* registry value seems OK, finish up and replace description */ - RegCloseKey (reghnd ); + RegCloseKey (reghnd ); sprintf (list[i].desc, "%s", regval); } } /* for */ #endif - return used; +#ifdef USE_TAP_NETWORK + if (used < max) { + list[used].num = used; +#if defined(__OpenBSD__) + sprintf(list[used].name, "%s", "tap:tunN"); +#else + sprintf(list[used].name, "%s", "tap:tapN"); +#endif + sprintf(list[used].desc, "%s", "Integrated Tun/Tap support"); + ++used; + } +#endif + + return used; } int eth_devices(int max, ETH_LIST* list) @@ -1467,4 +2588,35 @@ int eth_devices(int max, ETH_LIST* list) return i; } +void eth_show_dev (FILE *st, ETH_DEV* dev) +{ + fprintf(st, "Ethernet Device:\n"); + if (!dev) { + fprintf(st, "-- Not Attached\n"); + return; + } + fprintf(st, " Name: %s\n", dev->name); + fprintf(st, " Reflections: %d\n", dev->reflections); + fprintf(st, " Self Loopbacks Sent: %d\n", dev->loopback_self_sent_total); + fprintf(st, " Self Loopbacks Rcvd: %d\n", dev->loopback_self_rcvd_total); + if (dev->have_host_nic_phy_addr) { + char hw_mac[20]; + + eth_mac_fmt(&dev->host_nic_phy_hw_addr, hw_mac); + fprintf(st, " Host NIC Address: %s\n", hw_mac); + } + if (dev->jumbo_dropped) + fprintf(st, " Jumbo Dropped: %d\n", dev->jumbo_dropped); + if (dev->jumbo_fragmented) + fprintf(st, " Jumbo Fragmented: %d\n", dev->jumbo_fragmented); +#if defined(USE_READER_THREAD) + fprintf(st, " Asynch Interrupts: %s\n", dev->asynch_io?"Enabled":"Disabled"); + if (dev->asynch_io) + fprintf(st, " Interrupt Latency: %d uSec\n", dev->asynch_io_latency); + fprintf(st, " Read Queue: Count: %d\n", dev->read_queue.count); + fprintf(st, " Read Queue: High: %d\n", dev->read_queue.high); + fprintf(st, " Read Queue: Loss: %d\n", dev->read_queue.loss); + fprintf(st, " Peak Write Queue Size: %d\n", dev->write_queue_peak); +#endif +} #endif /* USE_NETWORK */ diff --git a/sim_ether.h b/sim_ether.h index 371ee3ba..4374f31e 100644 --- a/sim_ether.h +++ b/sim_ether.h @@ -28,6 +28,14 @@ Modification history: + 09-Dec-10 MP Added support to determine if network address conflicts exist + 07-Dec-10 MP Reworked DECnet self detection to the more general approach + of loopback self when any Physical Address is being set. + 04-Dec-10 MP Changed eth_write to do nonblocking writes when + USE_READER_THREAD is defined. + 07-Feb-08 MP Added eth_show_dev to display ethernet state + 28-Jan-08 MP Added eth_set_async + 23-Jan-08 MP Added eth_packet_trace_ex and ethq_destroy 30-Nov-05 DTH Added CRC length to packet and more field comments 04-Feb-04 DTH Added debugging information 14-Jan-04 MP Generalized BSD support issues @@ -64,7 +72,7 @@ #define USE_SETNONBLOCK 1 #endif -#if defined(__sun__) && defined(__i386__) +#if (((defined(__sun__) && defined(__i386__)) || defined(__linux)) && !defined(DONT_USE_READER_THREAD)) #define USE_READER_THREAD 1 #endif @@ -108,13 +116,20 @@ #define ETH_DEV_DESC_MAX 256 /* maximum device description size */ #define ETH_MIN_PACKET 60 /* minimum ethernet packet size */ #define ETH_MAX_PACKET 1514 /* maximum ethernet packet size */ +#define ETH_MAX_JUMBO_FRAME 16384 /* maximum ethernet jumbo frame size */ #define ETH_MAX_DEVICE 10 /* maximum ethernet devices */ #define ETH_CRC_SIZE 4 /* ethernet CRC size */ -#define ETH_FRAME_SIZE 1518 /* ethernet maximum frame size */ +#define ETH_FRAME_SIZE (ETH_MAX_PACKET+ETH_CRC_SIZE) /* ethernet maximum frame size */ +#define ETH_MIN_JUMBO_FRAME ETH_MAX_PACKET /* Threshold size for Jumbo Frame Processing */ -#define DECNET_SELF_FRAME(dnet_mac, msg) \ - ((memcmp(dnet_mac, msg , 6) == 0) && \ - (memcmp(dnet_mac, msg+6, 6) == 0)) +#define LOOPBACK_SELF_FRAME(phy_mac, msg) \ + ((memcmp(phy_mac, msg , 6) == 0) && \ + (memcmp(phy_mac, msg+6, 6) == 0) && \ + ((msg)[12] == 0x90) && ((msg)[13] == 0x00) && \ + ((msg)[14] == 0x00) && ((msg)[15] == 0x00) && \ + ((msg)[16] == 0x02) && ((msg)[17] == 0x00) && \ + (memcmp(phy_mac, msg+18, 6) == 0) && \ + ((msg)[24] == 0x01) && ((msg)[25] == 0x00)) struct eth_packet { uint8 msg[ETH_FRAME_SIZE]; /* ethernet frame (message) */ @@ -147,6 +162,7 @@ struct eth_list { typedef int ETH_BOOL; typedef unsigned char ETH_MAC[6]; +typedef unsigned char ETH_MULTIHASH[8]; typedef struct eth_packet ETH_PACK; typedef void (*ETH_PCALLBACK)(int status); typedef struct eth_list ETH_LIST; @@ -156,24 +172,45 @@ typedef struct eth_item ETH_ITEM; struct eth_device { char* name; /* name of ethernet device */ void* handle; /* handle of implementation-specific device */ + int fd_handle; /* fd to kernel device (where needed) */ + int pcap_mode; /* Flag indicating if pcap API are being used to move packets */ ETH_PCALLBACK read_callback; /* read callback function */ ETH_PCALLBACK write_callback; /* write callback function */ ETH_PACK* read_packet; /* read packet */ - ETH_PACK* write_packet; /* write packet */ ETH_MAC filter_address[ETH_FILTER_MAX]; /* filtering addresses */ int addr_count; /* count of filtering addresses */ ETH_BOOL promiscuous; /* promiscuous mode flag */ ETH_BOOL all_multicast; /* receive all multicast messages */ - int32 decnet_self_sent; /* loopback packets sent but not seen */ - ETH_MAC decnet_addr; /* decnet address of interface */ + ETH_BOOL hash_filter; /* filter using AUTODIN II multicast hash */ + ETH_MULTIHASH hash; /* AUTODIN II multicast hash */ + int32 loopback_self_sent; /* loopback packets sent but not seen */ + int32 loopback_self_sent_total; /* total loopback packets sent */ + int32 loopback_self_rcvd_total; /* total loopback packets seen */ + ETH_MAC physical_addr; /* physical address of interface */ + int32 have_host_nic_phy_addr; /* flag indicating that the host_nic_phy_hw_addr is valid */ + ETH_MAC host_nic_phy_hw_addr; /* MAC address of the attached NIC */ + uint32 jumbo_fragmented; /* Giant IPv4 Frames Fragmented */ + uint32 jumbo_dropped; /* Giant Frames Dropped */ DEVICE* dptr; /* device ethernet is attached to */ uint32 dbit; /* debugging bit */ int reflections; /* packet reflections on interface */ - int need_crc; /* device needs CRC (Cyclic Redundancy Check) */ + int need_crc; /* device needs CRC (Cyclic Redundancy Check) */ #if defined (USE_READER_THREAD) + int asynch_io; /* Asynchronous Interrupt scheduling enabled */ + int asynch_io_latency; /* instructions to delay pending interrupt */ ETH_QUE read_queue; pthread_mutex_t lock; pthread_t reader_thread; /* Reader Thread Id */ + pthread_t writer_thread; /* Writer Thread Id */ + pthread_mutex_t writer_lock; + pthread_cond_t writer_cond; + struct write_request { + struct write_request *next; + ETH_PACK packet; + } *write_requests; + int write_queue_peak; + struct write_request *write_buffers; + t_stat write_status; #endif }; @@ -186,18 +223,30 @@ t_stat eth_open (ETH_DEV* dev, char* name, /* open ethernet interfa t_stat eth_close (ETH_DEV* dev); /* close ethernet interface */ t_stat eth_write (ETH_DEV* dev, ETH_PACK* packet, /* write sychronous packet; */ ETH_PCALLBACK routine); /* callback when done */ -t_stat eth_read (ETH_DEV* dev, ETH_PACK* packet, /* read single packet; */ +int eth_read (ETH_DEV* dev, ETH_PACK* packet, /* read single packet; */ ETH_PCALLBACK routine); /* callback when done*/ t_stat eth_filter (ETH_DEV* dev, int addr_count, /* set filter on incoming packets */ - ETH_MAC* addresses, + ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous); +t_stat eth_filter_hash (ETH_DEV* dev, int addr_count, /* set filter on incoming packets with AUTODIN II based hash */ + ETH_MAC* const addresses, + ETH_BOOL all_multicast, + ETH_BOOL promiscuous, + ETH_MULTIHASH* const hash); +t_stat eth_check_address_conflict (ETH_DEV* dev, + ETH_MAC* const address); int eth_devices (int max, ETH_LIST* dev); /* get ethernet devices on host */ void eth_setcrc (ETH_DEV* dev, int need_crc); /* enable/disable CRC mode */ +t_stat eth_set_async (ETH_DEV* dev, int latency); /* set read behavior to be async */ +t_stat eth_clr_async (ETH_DEV* dev); /* set read behavior to be not async */ +uint32 eth_crc32(uint32 crc, const void* vbuf, size_t len); /* Compute Ethernet Autodin II CRC for buffer */ -void eth_packet_trace (ETH_DEV* dev, const uint8 *msg, int len, char* txt); /* trace ethernet packet */ +void eth_packet_trace (ETH_DEV* dev, const uint8 *msg, int len, char* txt); /* trace ethernet packet header+crc */ +void eth_packet_trace_ex (ETH_DEV* dev, const uint8 *msg, int len, char* txt, int detail, uint32 reason); /* trace ethernet packet */ t_stat eth_show (FILE* st, UNIT* uptr, /* show ethernet devices */ int32 val, void* desc); +void eth_show_dev (FILE*st, ETH_DEV* dev); /* show ethernet device state */ void eth_mac_fmt (ETH_MAC* add, char* buffer); /* format ethernet mac address */ t_stat eth_mac_scan (ETH_MAC* mac, char* strmac); /* scan string for mac, put in mac */ @@ -207,6 +256,10 @@ void ethq_clear (ETH_QUE* que); /* clear FIFO queue */ void ethq_remove (ETH_QUE* que); /* remove item from FIFO queue */ void ethq_insert (ETH_QUE* que, int32 type, /* insert item into FIFO queue */ ETH_PACK* packet, int32 status); +void ethq_insert_data(ETH_QUE* que, int32 type, /* insert item into FIFO queue */ + const uint8 *data, int used, int len, + int crc_len, const uint8 *crc_data, int32 status); +t_stat ethq_destroy(ETH_QUE* que); /* release FIFO queue */ #endif /* _SIM_ETHER_H */ diff --git a/sim_rev.h b/sim_rev.h index eeaf513e..8d7a623b 100644 --- a/sim_rev.h +++ b/sim_rev.h @@ -1,6 +1,6 @@ /* sim_rev.h: simulator revisions and current rev level - Copyright (c) 1993-2010, Robert M Supnik + Copyright (c) 1993-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -36,21 +36,43 @@ patch date module(s) and fix(es) - 2 tbd h316_cpu.c: + 2 tbd scp.c: + - added *nix READLINE support (Mark Pizzolato) + - fixed handling of DO with no arguments (Dave Bryan) + - clarified some help messages (Mark Pizzolato) + - added "SHOW SHOW" and "SHOW SHOW" commands (Mark Pizzolato) + - fixed bug in deposit stride for numeric input (John Dundas) + + sim_console.c + - added support for BREAK key on Windows (Mark Pizzolato) + + sim_ether.c + - major revision (Dave Hittner and Mark Pizzolato) + + sim_tmxr.c: + - made option negotiation more reliable (Mark Pizzolato) + + h316_cpu.c: - fixed bugs in MPY, DIV introduced in 3.8-1 i1401_cd.c: - fixed read stacker operation in column binary mode - - fixed punch stacker operation (from Van Snyder) + - fixed punch stacker operation (Van Snyder) - 1401_cpu.c: - - revised divide algorithm (from Van Snyder) + 1401_cpu.c: + - reverted multiple tape indicator implementation + - fixed EOT indicator test not to clear indicator (Van Snyder) + - fixed divide not to clear word marks in quotient (Van Snyder) + - revised divide algorithm (Van Snyder) i1401_mt.c: - - added no rewind option (from Van Snyder) + - reverted multiple tape indicator implementation + - fixed END indicator test not to clear indicator (Van Snyder) + - fixed backspace over tapemark not to set EOR (Van Snyder) + - added no rewind option (Van Snyder) pdp11_rk.c: - - fixed bug in read header (from Walter F Mueller) + - fixed bug in read header (Walter F Mueller) pdp11_rl.c: - added debug support @@ -58,6 +80,23 @@ patch date module(s) and fix(es) pdp11_rq.c: - added RD32 support + pdp11_tq.c: + - set UNIT_SXC flag when a tape mark is encountered + during forward motion read operations + - fixed logic which clears UNIT_SXC to check command modifier + - added CMF_WR flag to tq_cmf entry for OP_WTM + - made non-immediate rewind positioning operations take 2 seconds + - added UNIT_IDLE flag to tq units. + - fixed debug output of tape file positions when they are 64b + - added more debug output after positioning operations + - added textual display of the command being performed + (All of the above from Mark Pizzolato) + - fixed comments about register addresses + + pdp11_ts.c: + - fixed t_addr printouts for 64b big-endian systems + (from Mark Pizzolato) + pdp8_fpp.c: - many bug fixes (all from Rick Murphy); now functional @@ -65,32 +104,49 @@ patch date module(s) and fix(es) - added link to FPP vax_cpu.c: - - added OLDVMS idle timer option + - revised idle design (from Mark Pizzolato) - fixed bug in SET CPU IDLE + vax_cpu1.c: + - revised idle design (from Mark Pizzolato) + + vax_syscm.c: + - fixed t_addr printouts for 64b big-endian systems + (from Mark Pizzolato) + + vax_sysdev.c: + - added power clear call to boot routine (from Mark Pizzolato) + + vax780_sbi.c: + - added AUTORESTART switch support (from Mark Pizzolato) + + vax780_stddev.c + - added REBOOT support (from Mark Pizzolato) + + 1 08-Feb-09 scp.c: - revised RESTORE unit logic for consistency - - "detach_all" ignores error status returns if shutting down (from Dave Bryan) - - DO cmd missing params now default to null string (from Dave Bryan) - - DO cmd sub_args now allows "\\" to specify literal backslash (from Dave Bryan) + - "detach_all" ignores error status returns if shutting down (Dave Bryan) + - DO cmd missing params now default to null string (Dave Bryan) + - DO cmd sub_args now allows "\\" to specify literal backslash (Dave Bryan) - decommitted MTAB_VAL - fixed implementation of MTAB_NC - fixed warnings in help printouts sim_tape.c: - - fixed signed/unsigned warning in sim_tape_set_fmt (from Dave Bryan) + - fixed signed/unsigned warning in sim_tape_set_fmt (Dave Bryan) sim_tmxr.c, sim_tmxr.h: - added line connection order to tmxr_poll_conn, - added tmxr_set_lnorder and tmxr_show_lnorder (from Dave Bryan) - - print device and line to which connection was made (from Dave Bryan) + added tmxr_set_lnorder and tmxr_show_lnorder (Dave Bryan) + - print device and line to which connection was made (Dave Bryan) - added three new standardized SHOW routines all terminal multiplexers: - revised for new common SHOW routines in TMXR library - rewrote set size routines not to use MTAB_VAL - hp2100_cpu.c (from Dave Bryan): + hp2100_cpu.c (Dave Bryan): - VIS and IOP are now mutually exclusive on 1000-F - Removed A/B shadow register variables - Moved hp_setdev, hp_showdev to hp2100_sys.c @@ -106,91 +162,91 @@ patch date module(s) and fix(es) - Added SET CPU IDLE/NOIDLE, idle detection for DOS/RTE - Breakpoints on interrupt trap cells now work - hp2100_cpu0.c (from Dave Bryan): + hp2100_cpu0.c (Dave Bryan): - .FLUN and self-tests for VIS and SIGNAL are NOP if not present - Moved microcode function prototypes to hp2100_cpu1.h - Removed option-present tests (now in UIG dispatchers) - Added "user microcode" dispatcher for unclaimed instructions - hp2100_cpu1.c (from Dave Bryan): + hp2100_cpu1.c (Dave Bryan): - Moved microcode function prototypes to hp2100_cpu1.h - Moved option-present tests to UIG dispatchers - Call "user microcode" dispatcher for unclaimed UIG instructions - hp2100_cpu2.c (from Dave Bryan): + hp2100_cpu2.c (Dave Bryan): - Moved microcode function prototypes to hp2100_cpu1.h - Removed option-present tests (now in UIG dispatchers) - Updated mp_dms_jmp calling sequence - Fixed DJP, SJP, and UJP jump target validation - RVA/B conditionally updates dms_vr before returning value - hp2100_cpu3.c (from Dave Bryan): + hp2100_cpu3.c (Dave Bryan): - Moved microcode function prototypes to hp2100_cpu1.h - Removed option-present tests (now in UIG dispatchers) - Updated mp_dms_jmp calling sequence - hp2100_cpu4.c, hp2100_cpu7.c (from Dave Bryan): + hp2100_cpu4.c, hp2100_cpu7.c (Dave Bryan): - Moved microcode function prototypes to hp2100_cpu1.h - Removed option-present tests (now in UIG dispatchers) - hp2100_cpu5.c (from Dave Bryan): + hp2100_cpu5.c (Dave Bryan): - Moved microcode function prototypes to hp2100_cpu1.h - Removed option-present tests (now in UIG dispatchers) - Redefined ABORT to pass address, moved def to hp2100_cpu.h - Rewrote device I/O to model backplane signals - hp2100_cpu6.c (from Dave Bryan): + hp2100_cpu6.c (Dave Bryan): - Corrected .SIP debug formatting - Moved microcode function prototypes to hp2100_cpu1.h - Removed option-present tests (now in UIG dispatchers) - Rewrote device I/O to model backplane signals - hp2100 all peripherals (from Dave Bryan): + hp2100 all peripherals (Dave Bryan): - Rewrote device I/O to model backplane signals - hp2100_baci.c (from Dave Bryan): + hp2100_baci.c (Dave Bryan): - Fixed STC,C losing interrupt request on BREAK - Changed Telnet poll to connect immediately after reset or attach - Added REG_FIT to register variables < 32-bit size - Moved fmt_char() function to hp2100_sys.c - hp2100_dp.c, hp2100_dq.c (from Dave Bryan): + hp2100_dp.c, hp2100_dq.c (Dave Bryan): - Added REG_FIT to register variables < 32-bit size - hp2100_dr.c (from Dave Bryan): + hp2100_dr.c (Dave Bryan): - Revised drc_boot to use ibl_copy - hp2100_fp1.c (from Dave Bryan): + hp2100_fp1.c (Dave Bryan): - Quieted bogus gcc warning in fp_exec - hp2100_ipl.c (from Dave Bryan): + hp2100_ipl.c (Dave Bryan): - Changed socket poll to connect immediately after reset or attach - Revised EDT handler to refine completion delay conditions - Revised ipl_boot to use ibl_copy - hp2100_lpt.c (from Dave Bryan): + hp2100_lpt.c (Dave Bryan): - Changed CTIME register width to match documentation - hp2100_mpx.c (from Dave Bryan): + hp2100_mpx.c (Dave Bryan): - Implemented 12792C eight-channel terminal multiplexer - hp2100_ms.c (from Dave Bryan): + hp2100_ms.c (Dave Bryan): - Revised to use AR instead of saved_AR in boot - hp2100_mt.c (from Dave Bryan): + hp2100_mt.c (Dave Bryan): - Fixed missing flag after CLR command - Moved write enable and format commands from MTD to MTC - hp2100_mux.c (from Dave Bryan): + hp2100_mux.c (Dave Bryan): - SHOW MUX CONN/STAT with SET MUX DIAG is no longer disallowed - Changed Telnet poll to connect immediately after reset or attach - Added LINEORDER support - Added BREAK deferral to allow RTE break-mode to work - hp2100_pif.c (from Dave Bryan): + hp2100_pif.c (Dave Bryan): - Implemented 12620A/12936A Privileged Interrupt Fences - hp2100_sys.c (from Dave Bryan): + hp2100_sys.c (Dave Bryan): - Fixed IAK instruction dual-use mnemonic display - Moved hp_setdev, hp_showdev from hp2100_cpu.c - Changed sim_load to use WritePW instead of direct M[] access @@ -198,14 +254,14 @@ patch date module(s) and fix(es) - Moved fmt_char() function from hp2100_baci.c - Added MPX device - hp2100_cpu.h (from Dave Bryan): + hp2100_cpu.h (Dave Bryan): - Rearranged declarations with hp2100_cpu.c and hp2100_defs.h - Added mp_control to CPU state externals - hp2100_cpu1.h (from Dave Bryan): + hp2100_cpu1.h (Dave Bryan): - Moved microcode function prototypes here - hp2100_defs.h (from Dave Bryan): + hp2100_defs.h (Dave Bryan): - Added POLL_FIRST to indicate immediate connection attempt - Rearranged declarations with hp2100_cpu.h - Added PIF device @@ -213,22 +269,22 @@ patch date module(s) and fix(es) - Added MPX device i1401_cpu.c: - - fixed bug in ZA and ZS (from Bob Abeles) - - fixed tape indicator implementation (from Bob Abeles) - - added missing magtape modifier A (from Van Snyder) + - fixed bug in ZA and ZS (Bob Abeles) + - fixed tape indicator implementation (Bob Abeles) + - added missing magtape modifier A (Van Snyder) i1401_mt.c: - - added -n (no rewind) option to BOOT (from Van Snyder) - - fixed bug to mask input to 6b on read (from Bob Abeles) + - added -n (no rewind) option to BOOT (Van Snyder) + - fixed bug to mask input to 6b on read (Bob Abeles) lgp_stddev.c: - changed encode character from # to !, due to overlap pdp11_cpu.c: - - fixed failure to clear cpu_bme on RESET (found by Walter Mueller) + - fixed failure to clear cpu_bme on RESET (Walter Mueller) pdp11_dz.c: - - added MTAB_NC modifier on SET LOG command (found by Walter Mueller) + - added MTAB_NC modifier on SET LOG command (Walter Mueller) pdp11_io.c, vax_io.c, vax780_uba.c: - revised to use PDP-11 I/O library @@ -247,20 +303,20 @@ patch date module(s) and fix(es) - modified to resync TODR on any clock reset 0 15-Jun-08 scp.c: - - fixed bug in local/global register search (found by Mark Pizzolato) - - fixed bug in restore of RO units (from Mark Pizzolato) - - added SET/SHO/NO BR with default argument (from Dave Bryan) + - fixed bug in local/global register search (Mark Pizzolato) + - fixed bug in restore of RO units (Mark Pizzolato) + - added SET/SHO/NO BR with default argument (Dave Bryan) sim_tmxr.c - - worked around Telnet negotiation problem with QCTerm (from Dave Bryan) + - worked around Telnet negotiation problem with QCTerm (Dave Bryan) gri_defs.h, gri_cpu.c, gri_sys.c: - added GRI-99 support - hp2100_baci.c (from Dave Bryan): + hp2100_baci.c (Dave Bryan): - Implemented 12966A Buffered Asynchronous Communications Interface simulator - hp2100_cpu.c (from Dave Bryan): + hp2100_cpu.c (Dave Bryan): - Memory ex/dep and bkpt type default to current map mode - Added SET CPU DEBUG and OS/VMA flags, enabled OS/VMA - Corrected MP W5 (JSB) jumper action, SET/SHOW reversal, @@ -270,49 +326,49 @@ patch date module(s) and fix(es) - Enabled SIGNAL instructions, SIG debug flag - Fixed single stepping through interrupts - hp2100_cpu0.c (from Dave Bryan and Holger Veit): + hp2100_cpu0.c (Dave Bryan and Holger Veit): - Removed and implemented "cpu_rte_vma" and "cpu_rte_os" - Removed and implemented "cpu_vis" and "cpu_signal" - Removed and implemented "cpu_rte_ema" - hp2100_cpu1.c (from Dave Bryan): + hp2100_cpu1.c (Dave Bryan): - Added fprint_ops, fprint_regs for debug printouts - Enabled DIAG as NOP on 1000 F-Series - Fixed VIS and SIGNAL to depend on the FPP and HAVE_INT64 - hp2100_cpu3.c (from Dave Bryan): + hp2100_cpu3.c (Dave Bryan): - Fixed unsigned divide bug in .DDI - Fixed unsigned multiply bug in .DMP - Added implementation of DBI self-test - hp2100_cpu4.c (from Dave Bryan): + hp2100_cpu4.c (Dave Bryan): - Fixed B register return bug in /CMRT - hp2100_cpu5.c (from Holger Veit): + hp2100_cpu5.c (Holger Veit): - Implemented RTE-6/VM Virtual Memory Area firmware - Implemented RTE-IV Extended Memory Area firmware - hp2100_cpu6.c (from Dave Bryan): + hp2100_cpu6.c (Dave Bryan): - Implemented RTE-6/VM OS accelerator firmware - hp2100_cpu7.c (from Holger Veit): + hp2100_cpu7.c (Holger Veit): - Implemented Vector Instruction Set and SIGNAL/1000 firmware - hp2100_ds.c (from Dave Bryan): + hp2100_ds.c (Dave Bryan): - Corrected and verified ioCRS action - Corrected DPTR register definition from FLDATA to DRDATA - hp2100_fp.c (from Mark Pizzolato) + hp2100_fp.c (Mark Pizzolato) - Corrected fp_unpack mantissa high-word return - hp2100_fp1.c (from Dave Bryan): + hp2100_fp1.c (Dave Bryan): - Reworked "complement" to avoid inlining bug in gcc-4.x - Fixed uninitialized return in fp_accum when setting - hp2100_mux.c (from Dave Bryan): + hp2100_mux.c (Dave Bryan): - Sync mux poll with console poll for idle compatibility - hp2100_stddev.c (from Dave Bryan): + hp2100_stddev.c (Dave Bryan): - Fixed PTR trailing null counter for tape re-read - Added IPTICK register to CLK to display CPU instr/tick - Corrected and verified ioCRS actions @@ -322,24 +378,24 @@ patch date module(s) and fix(es) - Removed redundant control char handling definitions - Changed TTY output wait from 100 to 200 for MSU BASIC - hp2100_sys.c (from Dave Bryan): + hp2100_sys.c (Dave Bryan): - Added BACI device - Added RTE OS/VMA/EMA mnemonics - Changed fprint_sym to handle step with irq pending - hp2100_cpu.h (from Dave Bryan): + hp2100_cpu.h (Dave Bryan): - Added calc_defer() prototype - Added extern sim_deb, cpu_dev, DEB flags for debug printouts - Added extern intaddr, mp_viol, mp_mevff, calc_int, dev_ctl, ReadIO, WriteIO for RTE-6/VM microcode support - hp2100_cpu1.h (from Dave Bryan): + hp2100_cpu1.h (Dave Bryan): - Corrected OP_AFF to OP_AAFF for SIGNAL/1000 - Removed unused operand patterns - Added fprint_ops, fprint_regs for debug printouts - Revised OP_KKKAKK operand profile to OP_CCCACC for $LOC - hp2100_defs.h (from Dave Bryan): + hp2100_defs.h (Dave Bryan): - Added BACI device - Added 16/32-bit unsigned-to-signed conversions - Changed TMR_MUX to TMR_POLL for idle support @@ -347,43 +403,43 @@ patch date module(s) and fix(es) - Added I_MRG, I_ISZ, I_IOG, I_STF, and I_SFS instruction masks - Added I_MRG_I, I_JSB, I_JSB_I, and I_JMP instruction masks - nova_defs.h (from Bruce Ray): + nova_defs.h (Bruce Ray): - added support for third-party 64KW memory - nova_clk.c (from Bruce Ray): + nova_clk.c (Bruce Ray): - renamed to RTC, to match DG literature - nova_cpu.c (from Bruce Ray): + nova_cpu.c (Bruce Ray): - added support for third-party 64KW memory - added Nova 3 "secret" instructions - added CPU history support - nova_dkp.c (from Bruce Ray): + nova_dkp.c (Bruce Ray): - renamed to DKP, to match DG literature - fixed numerous bugs in both documented and undocumented behavior - changed bootstrap code to DG official sequence - nova_dsk.c (from Bruce Ray): + nova_dsk.c (Bruce Ray): - renamed to DSK, to match DG literature - added support for undocumented behavior - changed bootstrap code to DG official sequence - nova_mta.c (from Bruce Ray): + nova_mta.c (Bruce Ray): - renamed to MTA, to match DG literature - changed bootstrap code to DG official sequence - nova_plt.c, nova_pt.c (from Bruce Ray): + nova_plt.c, nova_pt.c (Bruce Ray): - added 7B/8B support - nova_sys.c (from Bruce Ray): + nova_sys.c (Bruce Ray): - fixed mistaken instruction mnemonics pdp11_cpu.c, pdp11_io.c, pdp11_rh.c: - - fixed DMA memory address limit test (found by John Dundas) - - fixed MMR0 treatment in RESET (found by Walter Mueller) + - fixed DMA memory address limit test (John Dundas) + - fixed MMR0 treatment in RESET (Walter Mueller) pdp11_cpumod.h, pdp11_cpumod.c: - - fixed write behavior of 11/70 MBRK, LOSIZE, HISIZE (found by Walter Mueller) + - fixed write behavior of 11/70 MBRK, LOSIZE, HISIZE (Walter Mueller) - added support to set default state of KDJ11B,E clock control register pdp11_dc.c: @@ -403,10 +459,10 @@ patch date module(s) and fix(es) pdp11_ke.c: - added support for KE11A - pdp11_kg.c (from John Dundas): + pdp11_kg.c (John Dundas): - added support for KG11A - pdp11_rc.c (from John Dundas): + pdp11_rc.c (John Dundas): - added support for RC11 pdp11_sys.c: @@ -415,7 +471,7 @@ patch date module(s) and fix(es) - renamed DL11 devices vax_cmode.c, vax_io.c, vax780_uba.c: - - fixed declarations (from Mark Pizzolato) + - fixed declarations (Mark Pizzolato) /* V3.7 revision history @@ -436,8 +492,8 @@ patch date module(s) and fix(es) vax_cpu.c: - fixed bug in read access g-format indexed specifiers - 2 12-Jul-07 sim_ether.c (from Dave Hittner): - - fixed non-ethernet device removal loop (from Naoki Hamada) + 2 12-Jul-07 sim_ether.c (Dave Hittner): + - fixed non-ethernet device removal loop (Naoki Hamada) - added dynamic loading of wpcap.dll; - corrected exceed max index bug in ethX lookup - corrected failure to look up ethernet device names in @@ -447,30 +503,30 @@ patch date module(s) and fix(es) - fixed idle timer event selection algorithm h316_lp.c: - - fixed loss of last print line (from Theo Engel) + - fixed loss of last print line (Theo Engel) h316_mt.c: - - fixed bug in write without stop (from Theo Engel) + - fixed bug in write without stop (Theo Engel) h316_stddev.c: - - fixed bug in clock increment (from Theo Engel) + - fixed bug in clock increment (Theo Engel) i1401_cpu.c: - added recognition of overlapped operation modifiers - remove restriction on load-mode binary tape operations i1401_mt.c: - - fixed read tape mark operation (found by Van Snyder) + - fixed read tape mark operation (Van Snyder) - remove restriction on load-mode binary tape operations pdp1_cpu.c: - - fixed typo in SBS clear (from Norm Lastovica) + - fixed typo in SBS clear (Norm Lastovica) pdp11_rh.c, pdp11_rp.c, pdp11_tu.c: - CS1 DVA is in the device, not the MBA pdp8_ct.c: - - fixed typo (from Norm Lastovica) + - fixed typo (Norm Lastovica) vax_cpu.c: - revised idle detector @@ -481,18 +537,18 @@ patch date module(s) and fix(es) - fixed bug in RESTORE with changed memory size - added global 'RESTORE in progress' flag - fixed breakpoint actions in DO command file processing - (from Dave Bryan) + (Dave Bryan) all CPU's with clocks: - removed clock initialization (now done in SCP) - hp2100_cpu.c (from Dave Bryan): + hp2100_cpu.c (Dave Bryan): - EDT passes input flag and DMA channel in dat parameter - hp2100_ipl.c (from Dave Bryan): + hp2100_ipl.c (Dave Bryan): - IPLI EDT delays DMA completion interrupt for TSB - hp2100_mux.c (from Dave Bryan): + hp2100_mux.c (Dave Bryan): - corrected "mux_sta" size from 16 to 21 elements - fixed "muxc_reset" to clear lines 16-20 - fixed control card OTx to set current channel number @@ -513,7 +569,7 @@ patch date module(s) and fix(es) - MR2 and MR3 only updated on NOP pdp10_tu.c, pdp11_tu.c: - - TMK sets FCE only on read (found by Naoki Hamada) + - TMK sets FCE only on read (Naoki Hamada) pdp11_xu.c: - added missing FC_RMAL command @@ -525,7 +581,7 @@ patch date module(s) and fix(es) vax780_defs.h - separated PxBR and SBR mbz checks - modified mbz checks to reflect 780 microcode patches - (found by Naoki Hamada) + (Naoki Hamada) vax_mmu.c: - added address masking to all SBR-based memory reads @@ -533,7 +589,7 @@ patch date module(s) and fix(es) 0 30-Jan-07 scp.c: - implemented throttle commands - added -e to control error processing in DO command files - (from Dave Bryan) + (Dave Bryan) sim_console.c: - fixed handling of non-printable characters in KSR mode @@ -550,7 +606,7 @@ patch date module(s) and fix(es) - fixed handling of non-printable characters in KSR mode hp2100_cpu.c, hp2100_cpu0.c, hp2100_cpu1.c, hp2100_cpu2.c, - hp2100_cpu3.c, hp2100_cpu4.c (from Dave Bryan): + hp2100_cpu3.c, hp2100_cpu4.c (Dave Bryan): - reorganized CPU modules for easier addition of new instructions - added Double Integer instructions, 1000-F CPU, 2114 and 2115 CPUs, 12K and 24K memory sizes, 12607B and 12578A DMA @@ -559,10 +615,10 @@ patch date module(s) and fix(es) - fixed indirect interrupt holdoff logic hp2100_ds.c: - - fixed REQUEST STATUS to clear status-1 (from Dave Bryan) + - fixed REQUEST STATUS to clear status-1 (Dave Bryan) hp2100_fp1.c: - - Added Floating Point Processor (from Dave Bryan) + - Added Floating Point Processor (Dave Bryan) hp2100_lps.c: - fixed diag-mode CLC response @@ -619,7 +675,7 @@ patch date module(s) and fix(es) - fixed bug in ASH -32 C value pdp11_rf.c: - - fixed unit mask (found by John Dundas) + - fixed unit mask (John Dundas) pdp11_stddev.c, vax_stddev.c, vax780_stddev.c: - synced keyboard poll to real-time clock @@ -636,7 +692,7 @@ patch date module(s) and fix(es) - synced service poll to real-time clock pdp11_sys.c: - - fixed operand order in EIS instructions (found by W.F.J. Mueller) + - fixed operand order in EIS instructions (W.F.J. Mueller) - added TA11 support pdp18b_cpu.c: @@ -666,7 +722,7 @@ patch date module(s) and fix(es) - added XVM RDCLK instruction pdp8_cpu.c: - - fixed SC value after DVI overflow (found by Don North) + - fixed SC value after DVI overflow (Don North) - added idle support and infinite loop detection pdp8_ct.c: @@ -687,7 +743,7 @@ patch date module(s) and fix(es) - added idle support vax_syscm.c: - - fixed operand order in EIS instructions (found by W.F.J. Mueller) + - fixed operand order in EIS instructions (W.F.J. Mueller) /* V3.6 revision history @@ -698,11 +754,11 @@ patch date module(s) and fix(es) all DECtapes: - fixed conflict in ATTACH switches - hp2100_ms.c (from Dave Bryan): + hp2100_ms.c (Dave Bryan): - added CAPACITY as alternate for REEL - fixed EOT test for unlimited reel size - i1620_cd.c (from Tom McBride): + i1620_cd.c (Tom McBride): - fixed card reader fgets call - fixed card reader boot sequence @@ -762,29 +818,29 @@ patch date module(s) and fix(es) most magtapes: - added support for finite reel size - h316_cpu.c: fixed bugs in LLL, LRL (found by Theo Engel) + h316_cpu.c: fixed bugs in LLL, LRL (Theo Engel) - h316_lp.c: fixed bug in blanks backscanning (found by Theo Engel) + h316_lp.c: fixed bug in blanks backscanning (Theo Engel) - h316_stddev.c: fixed bugs in punch state handling (found by Theo Engel) + h316_stddev.c: fixed bugs in punch state handling (Theo Engel) i1401_cpu.c: fixed bug in divide (reported by Van Snyder) - i16_cpu.c: fixed bug in DH (found by Mark Hittinger) + i16_cpu.c: fixed bug in DH (Mark Hittinger) i32_cpu.c: - - fixed bug in DH (found by Mark Hittinger) + - fixed bug in DH (Mark Hittinger) - added support for 8 register banks in 8/32 i7094: first release - id_io.c: fixed bug, GO preserves EXA and SSTA (found by Davis Johnson) + id_io.c: fixed bug, GO preserves EXA and SSTA (Davis Johnson) id_idc.c: - - fixed WD/WH handling (found by Davis Johnson) - - fixed bug, nop command should be ignored (found by Davis Johnson) + - fixed WD/WH handling (Davis Johnson) + - fixed bug, nop command should be ignored (Davis Johnson) - nova_cpu.c: fixed bug in DIVS (found by Mark Hittinger) + nova_cpu.c: fixed bug in DIVS (Mark Hittinger) pdp11_cis.c: (all reported by John Dundas) - fixed bug in decode table @@ -797,7 +853,7 @@ patch date module(s) and fix(es) pdp11_cr.c: added CR11/CD11 support pdp11_tc.c: - - fixed READ to set extended data bits in TCST (found by Alan Frisbie) + - fixed READ to set extended data bits in TCST (Alan Frisbie) vax780_fload.c: added FLOAD command @@ -876,7 +932,7 @@ patch date module(s) and fix(es) sim_tape.c, sim_tape.h: - added write support for P7B format - - fixed bug in write forward (found by Dave Bryan) + - fixed bug in write forward (Dave Bryan) h316_stddev.c, hp2100_stddev.c, hp2100_mux.c, id_tt.c, id_ttp.c, id_pas.c, pdp8_tt.c, pdp8_ttx.c, pdp11_stddev.c, @@ -904,16 +960,16 @@ patch date module(s) and fix(es) - changed default adapter to TM03 (for VMS) pdp8_df.c, pdp8_dt.c, pdp8_rf.c: - - fixed unaligned access bug (found by Doug Carman) + - fixed unaligned access bug (Doug Carman) - pdp8_rl.c: fixed IOT 61 decoding bug (found by David Gesswein) + pdp8_rl.c: fixed IOT 61 decoding bug (David Gesswein) vax_cpu.c: - fixed breakpoint detection when USE_ADDR64 option is active - fixed CVTfi to trap on integer overflow if PSW set 1 15-Oct-05 All CPU's, other sources: fixed declaration inconsistencies - (from Sterling Garwood) + (Sterling Garwood) i1401_cpu.c: added control for old/new character encodings @@ -933,7 +989,7 @@ patch date module(s) and fix(es) vax_io.c: fixed bug in autoconfiguration (missing XU) - vax_fpa.c: fixed bug in 32b structure definitions (from Jason Stevens) + vax_fpa.c: fixed bug in 32b structure definitions (Jason Stevens) 0 1-Sep-05 Note: most source modules have been edited to improve readability and to fix declaration and cast problems in C++ @@ -944,7 +1000,7 @@ patch date module(s) and fix(es) sim_sock.c: fixed SIGPIPE error on Unix - sim_ether.c: added Windows user-defined adapter names (from Timothe Litt) + sim_ether.c: added Windows user-defined adapter names (Timothe Litt) sim_tape.c: fixed misallocation of TPC map array @@ -977,7 +1033,7 @@ patch date module(s) and fix(es) - revised for new autoconfiguration interface - fixed bug in vector display routine - pdp11_xu.c: fixed runt packet processing (found by Tim Chapman) + pdp11_xu.c: fixed runt packet processing (Tim Chapman) pdp18b_cpu.c, pdp18b_sys.c: - removed spurious AAS instruction @@ -1004,28 +1060,28 @@ patch date module(s) and fix(es) 0 01-May-04 scp.c: - fixed ASSERT code - - revised syntax for SET DEBUG (from Dave Bryan) + - revised syntax for SET DEBUG (Dave Bryan) - revised interpretation of fprint_sym, fparse_sym returns - moved DETACH sanity tests into detach_unit sim_sock.h and sim_sock.c: - - added test for WSAEINPROGRESS (from Tim Riker) + - added test for WSAEINPROGRESS (Tim Riker) many: revised detach routines to test for attached state - hp2100_cpu.c: reorganized CPU options (from Dave Bryan) + hp2100_cpu.c: reorganized CPU options (Dave Bryan) - hp2100_cpu1.c: reorganized EIG routines (from Dave Bryan) + hp2100_cpu1.c: reorganized EIG routines (Dave Bryan) - hp2100_fp1.c: added FFP support (from Dave Bryan) + hp2100_fp1.c: added FFP support (Dave Bryan) id16_cpu.c: - - fixed bug in show history routine (from Mark Hittinger) + - fixed bug in show history routine (Mark Hittinger) - revised examine/deposit to do words rather than bytes id32_cpu.c: - fixed bug in initial memory allocation - - fixed bug in show history routine (from Mark Hittinger) + - fixed bug in show history routine (Mark Hittinger) - revised examine/deposit to do words rather than bytes id16_sys.c, id32_sys: @@ -1040,33 +1096,33 @@ patch date module(s) and fix(es) /* V3.3 revision history - 2 08-Mar-05 scp.c: added ASSERT command (from Dave Bryan) + 2 08-Mar-05 scp.c: added ASSERT command (Dave Bryan) h316_defs.h: fixed IORETURN macro - h316_mt.c: fixed error reporting from OCP (found by Philipp Hachtmann) + h316_mt.c: fixed error reporting from OCP (Philipp Hachtmann) - h316_stddev.c: fixed bug in OCP '0001 (found by Philipp Hachtmann) + h316_stddev.c: fixed bug in OCP '0001 (Philipp Hachtmann) hp2100_cpu.c: split out EAU and MAC instructions - hp2100_cpu1.c: (from Dave Bryan) + hp2100_cpu1.c: (Dave Bryan) - fixed missing MPCK on JRS target - removed EXECUTE instruction (is NOP in actual microcode) - hp2100_fp: (from Dave Bryan) + hp2100_fp: (Dave Bryan) - fixed missing negative overflow renorm in StoreFP i1401_lp.c: fixed bug in write_line (reported by Van Snyder) - id32_cpu.c: fixed branches to mask new PC (from Greg Johnson) + id32_cpu.c: fixed branches to mask new PC (Greg Johnson) pdp11_cpu.c: fixed bugs in RESET for 11/70 (reported by Tim Chapman) pdp11_cpumod.c: - - fixed bug in SHOW MODEL (from Sergey Okhapkin) - - made SYSID variable for 11/70 (from Tim Chapman) - - added MBRK write case for 11/70 (from Tim Chapman) + - fixed bug in SHOW MODEL (Sergey Okhapkin) + - made SYSID variable for 11/70 (Tim Chapman) + - added MBRK write case for 11/70 (Tim Chapman) pdp11_rq: added RA60, RA71, RA81 disks @@ -1089,13 +1145,13 @@ patch date module(s) and fix(es) h316_lp.c: fixed bug in DMA/DMC support hp2100_cpu.c: - - fixed DMA reset to clear alternate CTL flop (from Dave Bryan) - - fixed DMA reset to not clear control words (from Dave Bryan) + - fixed DMA reset to clear alternate CTL flop (Dave Bryan) + - fixed DMA reset to not clear control words (Dave Bryan) - fixed SBS, CBS, TBS to do virtual reads - - separated A/B from M[0/1], for DMA IO (from Dave Bryan) - - added SET CPU 21MX-M, 21MX-E (from Dave Brian) - - disabled TIMER/EXECUTE/DIAG instructions for 21MX-M (from Dave Bryan) - - added post-processor to maintain T/M consistency (from Dave Bryan) + - separated A/B from M[0/1], for DMA IO (Dave Bryan) + - added SET CPU 21MX-M, 21MX-E (Dave Brian) + - disabled TIMER/EXECUTE/DIAG instructions for 21MX-M (Dave Bryan) + - added post-processor to maintain T/M consistency (Dave Bryan) hp2100_ds.c: first release @@ -1109,13 +1165,13 @@ patch date module(s) and fix(es) hp2100_sys.c (all changes from Dave Bryan): - added STOP_OFFLINE, STOP_PWROFF messages - i1401_sys.c: added address argument support (from Van Snyder) + i1401_sys.c: added address argument support (Van Snyder) id_mt.c: added read-only file support lgp_cpu.c, lgp_sys.c: modified VM pointer setup - pdp11_cpu.c: fixed WAIT to work in all modes (from John Dundas) + pdp11_cpu.c: fixed WAIT to work in all modes (John Dundas) pdp11_tm.c, pdp11_ts.c: added read-only file support @@ -1123,9 +1179,9 @@ patch date module(s) and fix(es) 0 23-Nov-04 scp.c: - added reset_all_p (powerup) - - fixed comma-separated SET options (from Dave Bryan) - - changed ONLINE/OFFLINE to ENABLED/DISABLED (from Dave Bryan) - - modified to flush device buffers on stop (from Dave Bryan) + - fixed comma-separated SET options (Dave Bryan) + - changed ONLINE/OFFLINE to ENABLED/DISABLED (Dave Bryan) + - modified to flush device buffers on stop (Dave Bryan) - changed HELP to suppress duplicate command displays sim_console.c: @@ -1293,26 +1349,26 @@ patch date module(s) and fix(es) /* V3.2 revision history 3 03-Sep-04 scp.c: - - added ECHO command (from Dave Bryan) + - added ECHO command (Dave Bryan) - qualified RESTORE detach with SIM_SW_REST - sim_console: added OS/2 EMX fixes (from Holger Veit) + sim_console: added OS/2 EMX fixes (Holger Veit) - sim_sock.h: added missing definition for OS/2 (from Holger Veit) + sim_sock.h: added missing definition for OS/2 (Holger Veit) hp2100_cpu.c: changed error stops to report PC not PC + 1 - (from Dave Bryan) + (Dave Bryan) - hp2100_dp.c: functional and timing fixes (from Dave Bryan) + hp2100_dp.c: functional and timing fixes (Dave Bryan) - controller sets ATN for all commands except read status - controller resumes polling for ATN interrupts after read status - check status on unattached drive set busy and not ready - check status tests wrong unit for write protect status - drive on line sets ATN, will set FLG if polling - hp2100_dr.c: fixed CLC to stop operation (from Dave Bryan) + hp2100_dr.c: fixed CLC to stop operation (Dave Bryan) - hp2100_ms.c: functional and timing fixes (from Dave Bryan) + hp2100_ms.c: functional and timing fixes (Dave Bryan) - fixed erroneous execution of rejected command - fixed erroneous execution of select-only command - fixed erroneous execution of clear command @@ -1323,18 +1379,18 @@ patch date module(s) and fix(es) - added reel sizes to simulate end of tape - added debug printouts - hp2100_mt.c: modified handling of end of medium (from Dave Bryan) + hp2100_mt.c: modified handling of end of medium (Dave Bryan) - hp2100_stddev.c: added tab to control char set (from Dave Bryan) + hp2100_stddev.c: added tab to control char set (Dave Bryan) - pdp11_rq.c: VAX controllers luns start at 0 (from Andreas Cejna) + pdp11_rq.c: VAX controllers luns start at 0 (Andreas Cejna) vax_cpu.c: fixed bug in EMODD/G, second word of quad dst not probed 2 17-Jul-04 scp.c: fixed problem ATTACHing to read only files - (found by John Dundas) + (John Dundas) - sim_console.c: revised Windows console code (from Dave Bryan) + sim_console.c: revised Windows console code (Dave Bryan) sim_fio.c: fixed problem in big-endian read (reported by Scott Bailey) @@ -1342,13 +1398,13 @@ patch date module(s) and fix(es) gri_cpu.c: updated MSR, EAO functions hp_stddev.c: generalized handling of control char echoing - (from Dave Bryan) + (Dave Bryan) vax_sys.c: fixed bad block initialization routine 1 10-Jul-04 scp.c: added SET/SHOW CONSOLE subhierarchy - hp2100_cpu.c: fixes and added features (from Dave Bryan) + hp2100_cpu.c: fixes and added features (Dave Bryan) - SBT increments B after store - DMS console map must check dms_enb - SFS x,C and SFC x,C work @@ -1381,7 +1437,7 @@ patch date module(s) and fix(es) hp2100_dr.c: revised boot code to use IBL algorithm hp2100_mt.c, hp2100_ms.c: fixed spurious timing error after CLC - (found by Dave Bryan) + (Dave Bryan) hp2100_stddev.c: - fixed input behavior during typeout for RTE-IV @@ -1394,13 +1450,13 @@ patch date module(s) and fix(es) pdp11_tq.c: - fixed bug in reporting write protect (reported by Lyle Bickley) - - fixed TK70 model number and media ID (found by Robert Schaffrath) + - fixed TK70 model number and media ID (Robert Schaffrath) - pdp11_vh.c: added DHQ11 support (from John Dundas) + pdp11_vh.c: added DHQ11 support (John Dundas) - pdp11_io.c, vax_io.c: fixed DHQ11 autoconfigure (from John Dundas) + pdp11_io.c, vax_io.c: fixed DHQ11 autoconfigure (John Dundas) - pdp11_sys.c, vax_sys.c: added DHQ11 support (from John Dundas) + pdp11_sys.c, vax_sys.c: added DHQ11 support (John Dundas) vax_cpu.c: fixed bug in DIVBx, DIVWx (reported by Peter Trimmel) @@ -1434,7 +1490,7 @@ patch date module(s) and fix(es) all variable-sized devices: revised for sim_fsize change eclipse_cpu.c, nova_cpu.c: fixed device enable/disable support - (found by Bruce Ray) + (Bruce Ray) nova_defs.h, nova_sys.c, nova_qty.c: - added QTY and ALM support (Bruce Ray) @@ -1443,11 +1499,11 @@ patch date module(s) and fix(es) lgp: added LGP-30 [LGP-21] simulator - pdp1_sys.c: fixed bug in LOAD (found by Mark Crispin) + pdp1_sys.c: fixed bug in LOAD (Mark Crispin) pdp10_mdfp.c: - fixed bug in floating unpack - - fixed bug in FIXR (found by Philip Stone, fixed by Chris Smith) + - fixed bug in FIXR (Philip Stone, fixed by Chris Smith) pdp11_dz.c: added per-line logging @@ -1458,7 +1514,7 @@ patch date module(s) and fix(es) pdp11_hk.c, pdp11_rp.c: revised for device debug support - pdp11_rq.c: fixed bug in interrupt control (found by Tom Evans) + pdp11_rq.c: fixed bug in interrupt control (Tom Evans) pdp11_ry.c: added VAX support @@ -1536,9 +1592,9 @@ patch date module(s) and fix(es) - fixed bug, space operations return record count - fixed bug, reset doesn't cancel rewind - nova_sys.c: added floating point, timer support (from Charles Owen) + nova_sys.c: added floating point, timer support (Charles Owen) - i1620_cpu.c: fixed bug in branch digit (found by Dave Babcock) + i1620_cpu.c: fixed bug in branch digit (Dave Babcock) pdp1_drm.c: - added parallel drum support @@ -1556,7 +1612,7 @@ patch date module(s) and fix(es) pdp11_io.c: - added autoconfiguration controls - - fixed bug in I/O configuration (found by Dave Hittner) + - fixed bug in I/O configuration (Dave Hittner) pdp11_rq.c: - revised MB->LBN conversion for greater accuracy @@ -1581,7 +1637,7 @@ patch date module(s) and fix(es) pdp8_cpu.c: added instruction history pdp8_rx.c: - - fixed bug in RX28 read status (found by Charles Dickman) + - fixed bug in RX28 read status (Charles Dickman) - fixed double density write pdp8_td.c: added TD8E controller @@ -1594,7 +1650,7 @@ patch date module(s) and fix(es) vax_io.c: - added autoconfiguration controls - - fixed bug in I/O configuration (found by Dave Hittner) + - fixed bug in I/O configuration (Dave Hittner) id16_cpu.c: revised instruction decoding @@ -1625,7 +1681,7 @@ patch date module(s) and fix(es) - added diagnostic read (space forward) i1620_cpu.c - - fixed bug in immediate index add (found by Michael Short) + - fixed bug in immediate index add (Michael Short) 1 27-Jul-03 pdp1_cpu.c: updated to detect indefinite I/O wait @@ -1643,7 +1699,7 @@ patch date module(s) and fix(es) pdp10_rp.c: fixed bug in read header - pdp11_rq: fixed bug in user disk size (found by Chaskiel M Grundman) + pdp11_rq: fixed bug in user disk size (Chaskiel M Grundman) pdp18b_cpu.c: - added FP15 support @@ -1717,7 +1773,7 @@ patch date module(s) and fix(es) pdp10_rp.c: fixed ordering bug in attach pdp11_cpu.c: - - fixed bug in MMR1 update (found by Tim Stark) + - fixed bug in MMR1 update (Tim Stark) - fixed bug in memory size table pdp11_lp.c, pdp11_rq.c: added extended file support @@ -1742,8 +1798,8 @@ patch date module(s) and fix(es) vax_io.c: optimized byte and word DMA routines vax_sysdev.c: - - added calibrated delay to ROM reads (from Mark Pizzolato) - - fixed calibration problems in interval timer (from Mark Pizzolato) + - added calibrated delay to ROM reads (Mark Pizzolato) + - fixed calibration problems in interval timer (Mark Pizzolato) pdp1_dt.c: fixed variable size interaction with restore @@ -1764,7 +1820,7 @@ patch date module(s) and fix(es) - fixed bug in read status (13210A controller) - fixed bug in seek completion - id_pt.c: fixed type declaration (found by Mark Pizzolato) + id_pt.c: fixed type declaration (Mark Pizzolato) gri_cpu.c: fixed bug in SC queue pointer management @@ -1804,7 +1860,7 @@ patch date module(s) and fix(es) pdp10_tu.c: revised to use magtape library - pdp11_cpu.c: fixed bug in MMR1 update (found by Tim Stark) + pdp11_cpu.c: fixed bug in MMR1 update (Tim Stark) pdp11_stddev.c - added set line frequency command @@ -1826,7 +1882,7 @@ patch date module(s) and fix(es) - added variable controller, user defined drive support - revised to use magtape library - pdp18b_cpu.c: fixed three EAE bugs (found by Hans Pufal) + pdp18b_cpu.c: fixed three EAE bugs (Hans Pufal) pdp18b_mt.c: - fixed bugs in BOT error handling, interrupt handling @@ -1840,7 +1896,7 @@ patch date module(s) and fix(es) - added set line frequency command - added set ctrl-c command - pdp18b_sys.c: fixed FMTASC printouts (found by Hans Pufal) + pdp18b_sys.c: fixed FMTASC printouts (Hans Pufal) pdp8_clk.c: added set line frequency command @@ -1874,7 +1930,7 @@ patch date module(s) and fix(es) LP09 printer pdp18b_rf.c: - - fixed IOT decoding (found by Hans Pufal) + - fixed IOT decoding (Hans Pufal) - fixed address overrun logic - added variable number of platters and autosizing @@ -1892,18 +1948,18 @@ patch date module(s) and fix(es) nova_dsk.c: added variable number of platters and autosizing - id16_cpu.c: fixed bug in SETM, SETMR (found by Mark Pizzolato) + id16_cpu.c: fixed bug in SETM, SETMR (Mark Pizzolato) 2 15-Jan-03 scp.c: - added dynamic memory size flag and RESTORE support - added EValuate command - added get_ipaddr routine - - added ! (OS command) feature (from Mark Pizzolato) - - added BREAK support to sim_poll_kbd (from Mark Pizzolato) + - added ! (OS command) feature (Mark Pizzolato) + - added BREAK support to sim_poll_kbd (Mark Pizzolato) sim_tmxr.c: - - fixed bugs in IAC+IAC handling (from Mark Pizzolato) - - added IAC+BRK handling (from Mark Pizzolato) + - fixed bugs in IAC+IAC handling (Mark Pizzolato) + - added IAC+BRK handling (Mark Pizzolato) sim_sock.c: - added use count for Windows start/stop @@ -1929,12 +1985,12 @@ patch date module(s) and fix(es) pdp11_stddev.c: changed default to 7b (for early UNIX) vax_cpu.c, vax_io.c, vax_stddev.c, vax_sysdev.c: - added console halt capability (from Mark Pizzolato) + added console halt capability (Mark Pizzolato) all terminals and multiplexors: added BREAK support 1 21-Nov-02 pdp1_stddev.c: changed typewriter to half duplex - (found by Derek Peschel) + (Derek Peschel) pdp10_tu.c: - fixed bug in bootstrap (reported by Michael Thompson) @@ -1946,10 +2002,10 @@ patch date module(s) and fix(es) - removed VT emulation support - added support for statically buffered devices - added HELP - - fixed bugs in set_logon, ssh_break (found by David Hittner) - - added VMS file optimization (from Robert Alan Byer) + - fixed bugs in set_logon, ssh_break (David Hittner) + - added VMS file optimization (Robert Alan Byer) - added quiet mode, DO with parameters, GUI interface, - extensible commands (from Brian Knittel) + extensible commands (Brian Knittel) - added DEVICE context and flags - added central device enable/disable support - modified SAVE/GET to save and restore flags @@ -1961,7 +2017,7 @@ patch date module(s) and fix(es) - modified for Telnet console support - fixed bug in binary (8b) support sim_sock.c: modified for Telnet console support - sim_ether.c: new library for Ethernet (from David Hittner) + sim_ether.c: new library for Ethernet (David Hittner) all magtapes: - added support for end of medium @@ -1995,7 +2051,7 @@ patch date module(s) and fix(es) pdp11_ry.c: added RX211/RX02 support pdp11_hk.c: added RK611/RK06/RK07 support pdp11_tq.c: added TMSCP support - pdp11_xq.c: added DEQNA/DELQA support (from David Hittner) + pdp11_xq.c: added DEQNA/DELQA support (David Hittner) pdp11_pclk.c: added KW11P support pdp11_ts.c: - fixed bug in CTL decoding @@ -2019,7 +2075,7 @@ patch date module(s) and fix(es) vax_stddev.c: removed paper tape, now uses PDP-11 version vax_sysdev.c: - allowed NVR to be attached to file - - removed unused variables (found by David Hittner) + - removed unused variables (David Hittner) PDP-10 pdp10_defs.h, pdp10_ksio.c, all peripherals: @@ -2069,12 +2125,12 @@ patch date module(s) and fix(es) /* V2.9 revision history 11 20-Jul-02 i1401_mt.c: on read, end of record stores group mark - without word mark (found by Van Snyder) + without word mark (Van Snyder) i1401_dp.c: reworked address generation and checking vax_cpu.c: added infinite loop detection and halt to - boot ROM option (from Mark Pizzolato) + boot ROM option (Mark Pizzolato) vax_fpa.c: changed function names to prevent conflict with C math library @@ -2083,31 +2139,31 @@ patch date module(s) and fix(es) John Dundas) pdp18b_stddev.c: added "ASCII mode" for reader and - punch (from Hans Pufal) + punch (Hans Pufal) gri_*.c: added GRI-909 simulator - scp.c: added DO echo, DO exit (from Brian Knittel) + scp.c: added DO echo, DO exit (Brian Knittel) scp_tty.c: added Windows priority hacking (from Mark Pizzolato) 10 15-Jun-02 scp.c: fixed error checking on calls to fxread/fxwrite - (found by Norm Lastovic) + (Norm Lastovic) scp_tty.c, sim_vt.h, sim_vt.c: added VTxxx emulation - support for Windows (from Fischer Franz) + support for Windows (Fischer Franz) - sim_sock.c: added OS/2 support (from Holger Veit) + sim_sock.c: added OS/2 support (Holger Veit) - pdp11_cpu.c: fixed bugs (from John Dundas) + pdp11_cpu.c: fixed bugs (John Dundas) - added special case for PS<15:12> = 1111 to MFPI - removed special case from MTPI - added masking of relocation adds i1401_cpu.c: - added multiply/divide - - fixed bugs (found by Van Snyder) + - fixed bugs (Van Snyder) o 5 and 7 character H, 7 character doesn't branch o 8 character NOP o 1401-like memory dump @@ -2133,9 +2189,9 @@ patch date module(s) and fix(es) vax_cpu1.c: fixed exception flows to clear trap request 7 30-Apr-02 scp.c: fixed bug in clock calibration when (real) clock - jumps forward due too far (found by Jonathan Engdahl) + jumps forward due too far (Jonathan Engdahl) - pdp11_cpu.c: fixed bugs, added features (from John Dundas + pdp11_cpu.c: fixed bugs, added features (John Dundas and Wolfgang Helbig) - added HTRAP and BPOK to maintenance register - added trap on kernel HALT if MAINT set @@ -2167,8 +2223,8 @@ patch date module(s) and fix(es) vax_fpu.c: fixed EDIV overflow test for 0 quotient 5 14-Apr-02 vax_cpu1.c: - - fixed interrupt, prv_mode set to 0 (found by Tim Stark) - - fixed PROBEx to mask mode to 2b (found by Kevin Handy) + - fixed interrupt, prv_mode set to 0 (Tim Stark) + - fixed PROBEx to mask mode to 2b (Kevin Handy) 4 1-Apr-02 pdp11_rq.c: fixed bug, reset cleared write protect status @@ -2218,7 +2274,7 @@ patch date module(s) and fix(es) - added -e switch to attach - moved device enable/disable to simulators - scp_tty.c: VAX specific fix (from Robert Alan Byer) + scp_tty.c: VAX specific fix (Robert Alan Byer) sim_tmxr.c, sim_tmxr.h: - added tmxr_fstats, tmxr_dscln @@ -2235,19 +2291,19 @@ patch date module(s) and fix(es) pdp8_ttx.c: rewrote as unified multiplexor - pdp11_cpu.c: fixed calc_MMR1 macro (found by Robert Alan Byer) + pdp11_cpu.c: fixed calc_MMR1 macro (Robert Alan Byer) - pdp11_stddev.c: fixed bugs in KW11L (found by John Dundas) + pdp11_stddev.c: fixed bugs in KW11L (John Dundas) pdp11_rp.c: fixed bug in 18b mode boot pdp11 bootable I/O devices: fixed register setup at boot - exit (found by Doug Carman) + exit (Doug Carman) hp2100_cpu.c: - - fixed DMA register tables (found by Bill McDermith) - - fixed SZx,SLx,RSS bug (found by Bill McDermith) - - fixed flop restore logic (found by Bill McDermith) + - fixed DMA register tables (Bill McDermith) + - fixed SZx,SLx,RSS bug (Bill McDermith) + - fixed flop restore logic (Bill McDermith) hp2100_mt.c: fixed bug on write of last character @@ -2255,10 +2311,10 @@ patch date module(s) and fix(es) multiplexor controllers i1401_cd.c, i1401_mt.c: new zero footprint bootstraps - (from Van Snyder) + (Van Snyder) i1401_sys.c: fixed symbolic display of H, NOP with no trailing - word mark (found by Van Snyder) + word mark (Van Snyder) most CPUs: - replaced OLDPC with PC queue @@ -2266,7 +2322,7 @@ patch date module(s) and fix(es) V2.8 revision history -5 25-Dec-01 scp.c: fixed bug in DO command (found by John Dundas) +5 25-Dec-01 scp.c: fixed bug in DO command (John Dundas) pdp10_cpu.c: - moved trap-in-progress to separate variable @@ -2302,7 +2358,7 @@ patch date module(s) and fix(es) pdp8: added RL8A - pdp10: fixed two ITS-related bugs (found by Dave Conroy) + pdp10: fixed two ITS-related bugs (Dave Conroy) V2.7 revision history @@ -2316,11 +2372,11 @@ patch date module(s) and fix(es) to use symbolic base addresses and lengths 14 20-Oct-01 dec_dz.h, sim_tmxr_h, sim_tmxr.c: fixed bug in Telnet - state handling (found by Thord Nilson), removed + state handling (Thord Nilson), removed tmxr_getchar, added tmxr_rqln and tmxr_tqln 13 18-Oct-01 pdp11_tm.c: added stub diagnostic register clock - for RSTS/E (found by Thord Nilson) + for RSTS/E (Thord Nilson) 12 15-Oct-01 pdp11_defs.h, pdp11_cpu.c, pdp11_tc.c, pdp11_ts.c, pdp11_rp.c: added operations logging diff --git a/sim_sock.c b/sim_sock.c index a5579a7c..63c0f573 100644 --- a/sim_sock.c +++ b/sim_sock.c @@ -1,6 +1,6 @@ /* sim_sock.c: OS-dependent socket routines - Copyright (c) 2001-2008, Robert M Supnik + Copyright (c) 2001-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 22-Jun-10 RMS Fixed types in sim_accept_conn (from Mark Pizzolato) 19-Nov-05 RMS Added conditional for OpenBSD (from Federico G. Schwindt) 16-Aug-05 RMS Fixed spurious SIGPIPE signal error in Unix 14-Apr-05 RMS Added WSAEINPROGRESS test (from Tim Riker) @@ -195,9 +196,10 @@ SOCKET sim_accept_conn (SOCKET master, uint32 *ipaddr) { int32 sta, err; #if defined (macintosh) || defined (__linux) || \ - defined (__APPLE__) || defined (__OpenBSD__) + defined (__APPLE__) || defined (__OpenBSD__) || \ + defined(__NetBSD__) || defined(__FreeBSD__) socklen_t size; -#elif defined (_WIN32) || defined (__EMX__) ||\ +#elif defined (_WIN32) || defined (__EMX__) || \ (defined (__ALPHA) && defined (__unix__)) int size; #else @@ -216,7 +218,8 @@ if (newsock == INVALID_SOCKET) { /* error? */ printf ("Sockets: accept error %d\n", err); return INVALID_SOCKET; } -if (ipaddr != NULL) *ipaddr = ntohl (clientname.sin_addr.s_addr); +if (ipaddr != NULL) + *ipaddr = ntohl (clientname.sin_addr.s_addr); sta = sim_setnonblock (newsock); /* set nonblocking */ if (sta == SOCKET_ERROR) /* fcntl error? */ diff --git a/sim_timer.c b/sim_timer.c index af84fd48..783661e1 100644 --- a/sim_timer.c +++ b/sim_timer.c @@ -1,6 +1,6 @@ /* sim_timer.c: simulator timer library - Copyright (c) 1993-2008, Robert M Supnik + Copyright (c) 1993-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 29-Dec-10 MP Fixed clock resolution determination for Unix platforms 22-Sep-08 RMS Added "stability threshold" for idle routine 27-May-08 RMS Fixed bug in Linux idle routines (from Walter Mueller) 18-Jun-07 RMS Modified idle to exclude counted delays @@ -298,19 +299,6 @@ return; uint32 sim_os_ms_sleep_init (void) { -#if defined (_POSIX_SOURCE) /* POSIX-compliant */ - -struct timespec treq; -uint32 msec; - -if (clock_getres (CLOCK_REALTIME, &treq) != 0) - return 0; -msec = (treq.tv_nsec + (NANOS_PER_MILLI - 1)) / NANOS_PER_MILLI; -if (msec > SIM_IDLE_MAX) return 0; -return msec; - -#else /* others */ - uint32 i, t1, t2, tot, tim; for (i = 0, tot = 0; i < sleep1Samples; i++) { @@ -320,13 +308,9 @@ for (i = 0, tot = 0; i < sleep1Samples; i++) { tot += (t2 - t1); } tim = (tot + (sleep1Samples - 1)) / sleep1Samples; -if (tim == 0) - tim = 1; -else if (tim > SIM_IDLE_MAX) +if (tim > SIM_IDLE_MAX) tim = 0; return tim; - -#endif } uint32 sim_os_ms_sleep (unsigned int milliseconds) diff --git a/sim_tmxr.c b/sim_tmxr.c index b7ff0fdd..a52335da 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -1,6 +1,6 @@ /* sim_tmxr.c: Telnet terminal multiplexor library - Copyright (c) 2001-2008, Robert M Supnik + Copyright (c) 2001-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,6 +26,7 @@ Based on the original DZ11 simulator by Thord Nilson, as updated by Arthur Krewat. + 16-Jan-11 MP Made option negotiation more reliable 20-Nov-08 RMS Added three new standardized SHOW routines 30-Sep-08 JDB Reverted tmxr_find_ldsc to original implementation 27-May-08 JDB Added line connection order to tmxr_poll_conn, @@ -86,12 +87,26 @@ /* Telnet protocol constants - negatives are for init'ing signed char data */ +/* Commands */ #define TN_IAC -1 /* protocol delim */ #define TN_DONT -2 /* dont */ #define TN_DO -3 /* do */ #define TN_WONT -4 /* wont */ #define TN_WILL -5 /* will */ +#define TN_SB -6 /* sub-option negotiation */ +#define TN_GA -7 /* go ahead */ +#define TN_EL -8 /* erase line */ +#define TN_EC -9 /* erase character */ +#define TN_AYT -10 /* are you there */ +#define TN_AO -11 /* abort output */ +#define TN_IP -12 /* interrupt process */ #define TN_BRK -13 /* break */ +#define TN_DATAMK -14 /* data mark */ +#define TN_NOP -15 /* no operation */ +#define TN_SE -16 /* end sub-option negot */ + +/* Options */ + #define TN_BIN 0 /* bin */ #define TN_ECHO 1 /* echo */ #define TN_SGA 3 /* sga */ @@ -108,6 +123,8 @@ #define TNS_WONT 003 /* WONT seen */ #define TNS_SKIP 004 /* skip next cmd */ #define TNS_CRPAD 005 /* CR padding */ +#define TNS_DO 006 /* DO request pending rejection */ + void tmxr_rmvrc (TMLN *lp, int32 p); int32 tmxr_send_buffered_data (TMLN *lp); @@ -139,6 +156,7 @@ TMLN *lp; int32 *op; int32 i, j; uint32 ipaddr; + static char mantra[] = { TN_IAC, TN_WILL, TN_LINE, TN_IAC, TN_WILL, TN_SGA, @@ -178,7 +196,7 @@ if (newsock != INVALID_SOCKET) { /* got a live one? */ lp->tsta = 0; /* init telnet state */ lp->xmte = 1; /* enable transmit */ lp->dstb = 0; /* default bin mode */ - sim_write_sock (newsock, mantra, 15); + sim_write_sock (newsock, mantra, sizeof (mantra)); tmxr_linemsg (lp, "\n\r\nConnected to the "); tmxr_linemsg (lp, sim_name); tmxr_linemsg (lp, " simulator "); @@ -301,7 +319,7 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */ break; case TNS_IAC: /* IAC prev */ - if ((tmp == TN_IAC) & !lp->dstb) { /* IAC + IAC, bin? */ + if (tmp == TN_IAC) { /* IAC + IAC */ lp->tsta = TNS_NORM; /* treat as normal */ j = j + 1; /* advance j */ break; /* keep IAC */ @@ -313,11 +331,31 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */ j = j + 1; /* advance j */ break; } - if (tmp == TN_WILL) /* IAC + WILL? */ + switch (tmp) { + case TN_WILL: /* IAC + WILL? */ lp->tsta = TNS_WILL; - else if (tmp == TN_WONT) /* IAC + WONT? */ + break; + case TN_WONT: /* IAC + WONT? */ lp->tsta = TNS_WONT; - else lp->tsta = TNS_SKIP; /* IAC + other */ + break; + case TN_DO: /* IAC + DO? */ + lp->tsta = TNS_DO; + break; + case TN_DONT: /* IAC + DONT? */ + lp->tsta = TNS_SKIP; /* IAC + other */ + break; + case TN_GA: case TN_EL: /* IAC + other 2 byte types */ + case TN_EC: case TN_AYT: + case TN_AO: case TN_IP: + case TN_NOP: + lp->tsta = TNS_NORM; /* ignore */ + break; + case TN_SB: /* IAC + SB sub-opt negotiation */ + case TN_DATAMK: /* IAC + data mark */ + case TN_SE: /* IAC + SE sub-opt end */ + lp->tsta = TNS_NORM; /* ignore */ + break; + } tmxr_rmvrc (lp, j); /* remove char */ break; @@ -327,6 +365,9 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */ lp->dstb = 0; else lp->dstb = 1; } + tmxr_rmvrc (lp, j); /* remove it */ + lp->tsta = TNS_NORM; /* next normal */ + break; /* Negotiation with the HP terminal emulator "QCTerm" is not working. QCTerm says "WONT BIN" but sends bare CRs. RFC 854 says: @@ -349,9 +390,18 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */ tmxr_rmvrc (lp, j); /* remove it */ break; - case TNS_SKIP: default: /* skip char */ + case TNS_DO: /* pending DO request */ + if (tmp == TN_BIN) { /* reject all but binary mode */ + char accept[] = {TN_IAC, TN_WILL, TN_BIN}; + sim_write_sock (lp->conn, accept, sizeof(accept)); + } + tmxr_rmvrc (lp, j); /* remove it */ lp->tsta = TNS_NORM; /* next normal */ + break; + + case TNS_SKIP: default: /* skip char */ tmxr_rmvrc (lp, j); /* remove char */ + lp->tsta = TNS_NORM; /* next normal */ break; } /* end case state */ } /* end for char */ diff --git a/sim_tmxr_old.c b/sim_tmxr_old.c new file mode 100644 index 00000000..b7ff0fdd --- /dev/null +++ b/sim_tmxr_old.c @@ -0,0 +1,998 @@ +/* sim_tmxr.c: Telnet terminal multiplexor library + + Copyright (c) 2001-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + Based on the original DZ11 simulator by Thord Nilson, as updated by + Arthur Krewat. + + 20-Nov-08 RMS Added three new standardized SHOW routines + 30-Sep-08 JDB Reverted tmxr_find_ldsc to original implementation + 27-May-08 JDB Added line connection order to tmxr_poll_conn, + added tmxr_set_lnorder and tmxr_show_lnorder + 14-May-08 JDB Print device and line to which connection was made + 11-Apr-07 JDB Worked around Telnet negotiation problem with QCTerm + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 29-Jun-05 RMS Extended tmxr_dscln to support unit array devices + Fixed bug in SET LOG/NOLOG + 04-Jan-04 RMS Changed TMXR ldsc to be pointer to linedesc array + Added tmxr_linemsg, circular output pointers, logging + (from Mark Pizzolato) + 29-Dec-03 RMS Added output stall support + 01-Nov-03 RMS Cleaned up attach routine + 09-Mar-03 RMS Fixed bug in SHOW CONN + 22-Dec-02 RMS Fixed bugs in IAC+IAC receive and transmit sequences + Added support for received break (all from by Mark Pizzolato) + Fixed bug in attach + 31-Oct-02 RMS Fixed bug in 8b (binary) support + 22-Aug-02 RMS Added tmxr_open_master, tmxr_close_master + 30-Dec-01 RMS Added tmxr_fstats, tmxr_dscln, renamed tmxr_fstatus + 03-Dec-01 RMS Changed tmxr_fconns for extended SET/SHOW + 20-Oct-01 RMS Fixed bugs in read logic (found by Thord Nilson). + Added tmxr_rqln, tmxr_tqln + + This library includes: + + tmxr_poll_conn - poll for connection + tmxr_reset_ln - reset line + tmxr_getc_ln - get character for line + tmxr_poll_rx - poll receive + tmxr_putc_ln - put character for line + tmxr_poll_tx - poll transmit + tmxr_open_master - open master connection + tmxr_close_master - close master connection + tmxr_attach - attach terminal multiplexor + tmxr_detach - detach terminal multiplexor + tmxr_ex - (null) examine + tmxr_dep - (null) deposit + tmxr_msg - send message to socket + tmxr_linemsg - send message to line + tmxr_fconns - output connection status + tmxr_fstats - output connection statistics + tmxr_dscln - disconnect line (SET routine) + tmxr_rqln - number of available characters for line + tmxr_tqln - number of buffered characters for line + tmxr_set_lnorder - set line connection order + tmxr_show_lnorder - show line connection order + + All routines are OS-independent. +*/ + +#include "sim_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include "scp.h" +#include + +/* Telnet protocol constants - negatives are for init'ing signed char data */ + +#define TN_IAC -1 /* protocol delim */ +#define TN_DONT -2 /* dont */ +#define TN_DO -3 /* do */ +#define TN_WONT -4 /* wont */ +#define TN_WILL -5 /* will */ +#define TN_BRK -13 /* break */ +#define TN_BIN 0 /* bin */ +#define TN_ECHO 1 /* echo */ +#define TN_SGA 3 /* sga */ +#define TN_LINE 34 /* line mode */ +#define TN_CR 015 /* carriage return */ +#define TN_LF 012 /* line feed */ +#define TN_NUL 000 /* null */ + +/* Telnet line states */ + +#define TNS_NORM 000 /* normal */ +#define TNS_IAC 001 /* IAC seen */ +#define TNS_WILL 002 /* WILL seen */ +#define TNS_WONT 003 /* WONT seen */ +#define TNS_SKIP 004 /* skip next cmd */ +#define TNS_CRPAD 005 /* CR padding */ + +void tmxr_rmvrc (TMLN *lp, int32 p); +int32 tmxr_send_buffered_data (TMLN *lp); +TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, TMXR *mp); + +extern int32 sim_switches; +extern char sim_name[]; +extern FILE *sim_log; +extern uint32 sim_os_msec (void); + +/* Poll for new connection + + Called from unit service routine to test for new connection + + Inputs: + *mp = pointer to terminal multiplexor descriptor + Outputs: + line number activated, -1 if none + + If a connection order is defined for the descriptor, and the first value is + not -1 (indicating default order), then the order array is used to find an + open line. Otherwise, a search is made of all lines in numerical sequence. +*/ + +int32 tmxr_poll_conn (TMXR *mp) +{ +SOCKET newsock; +TMLN *lp; +int32 *op; +int32 i, j; +uint32 ipaddr; +static char mantra[] = { + TN_IAC, TN_WILL, TN_LINE, + TN_IAC, TN_WILL, TN_SGA, + TN_IAC, TN_WILL, TN_ECHO, + TN_IAC, TN_WILL, TN_BIN, + TN_IAC, TN_DO, TN_BIN + }; + +newsock = sim_accept_conn (mp->master, &ipaddr); /* poll connect */ +if (newsock != INVALID_SOCKET) { /* got a live one? */ + op = mp->lnorder; /* get line connection order list pointer */ + i = mp->lines; /* play it safe in case lines == 0 */ + + for (j = 0; j < mp->lines; j++, i++) { /* find next avail line */ + if (op && (*op >= 0) && (*op < mp->lines)) /* order list present and valid? */ + i = *op++; /* get next line in list to try */ + else /* no list or not used or range error */ + i = j; /* get next sequential line */ + + lp = mp->ldsc + i; /* get pointer to line descriptor */ + if (lp->conn == 0) /* is the line available? */ + break; /* yes, so stop search */ + } + + if (i >= mp->lines) { /* all busy? */ + tmxr_msg (newsock, "All connections busy\r\n"); + sim_close_sock (newsock, 0); + } + else { + lp = mp->ldsc + i; /* get line desc */ + lp->conn = newsock; /* record connection */ + lp->ipad = ipaddr; /* ip address */ + lp->cnms = sim_os_msec (); /* time of conn */ + lp->rxbpr = lp->rxbpi = 0; /* init buf pointers */ + lp->txbpr = lp->txbpi = 0; + lp->rxcnt = lp->txcnt = 0; /* init counters */ + lp->tsta = 0; /* init telnet state */ + lp->xmte = 1; /* enable transmit */ + lp->dstb = 0; /* default bin mode */ + sim_write_sock (newsock, mantra, 15); + tmxr_linemsg (lp, "\n\r\nConnected to the "); + tmxr_linemsg (lp, sim_name); + tmxr_linemsg (lp, " simulator "); + + if (mp->dptr) { /* device defined? */ + tmxr_linemsg (lp, sim_dname (mp->dptr)); /* report device name */ + tmxr_linemsg (lp, " device"); + + if (mp->lines > 1) { /* more than one line? */ + char line[20]; + + tmxr_linemsg (lp, ", line "); /* report the line number */ + sprintf (line, "%i", i); + tmxr_linemsg (lp, line); + } + } + + tmxr_linemsg (lp, "\r\n\n"); + + tmxr_poll_tx (mp); /* flush output */ + return i; + } + } /* end if newsock */ +return -1; +} + +/* Reset line */ + +void tmxr_reset_ln (TMLN *lp) +{ +if (lp->txlog) /* dump log */ + fflush (lp->txlog); +tmxr_send_buffered_data (lp); /* send buffered data */ +sim_close_sock (lp->conn, 0); /* reset conn */ +lp->conn = lp->tsta = 0; /* reset state */ +lp->rxbpr = lp->rxbpi = 0; +lp->txbpr = lp->txbpi = 0; +lp->xmte = 1; +lp->dstb = 0; +return; +} + +/* Get character from specific line + + Inputs: + *lp = pointer to terminal line descriptor + Output: + valid + char, 0 if line +*/ + +int32 tmxr_getc_ln (TMLN *lp) +{ +int32 j, val = 0; +uint32 tmp; + +if (lp->conn && lp->rcve) { /* conn & enb? */ + j = lp->rxbpi - lp->rxbpr; /* # input chrs */ + if (j) { /* any? */ + tmp = lp->rxb[lp->rxbpr]; /* get char */ + val = TMXR_VALID | (tmp & 0377); /* valid + chr */ + if (lp->rbr[lp->rxbpr]) /* break? */ + val = val | SCPE_BREAK; + lp->rxbpr = lp->rxbpr + 1; /* adv pointer */ + } + } /* end if conn */ +if (lp->rxbpi == lp->rxbpr) /* empty? zero ptrs */ + lp->rxbpi = lp->rxbpr = 0; +return val; +} + +/* Poll for input + + Inputs: + *mp = pointer to terminal multiplexor descriptor + Outputs: none +*/ + +void tmxr_poll_rx (TMXR *mp) +{ +int32 i, nbytes, j; +TMLN *lp; + +for (i = 0; i < mp->lines; i++) { /* loop thru lines */ + lp = mp->ldsc + i; /* get line desc */ + if (!lp->conn || !lp->rcve) /* skip if !conn */ + continue; + + nbytes = 0; + if (lp->rxbpi == 0) /* need input? */ + nbytes = sim_read_sock (lp->conn, /* yes, read */ + &(lp->rxb[lp->rxbpi]), /* leave spc for */ + TMXR_MAXBUF - TMXR_GUARD); /* Telnet cruft */ + else if (lp->tsta) /* in Telnet seq? */ + nbytes = sim_read_sock (lp->conn, /* yes, read to end */ + &(lp->rxb[lp->rxbpi]), + TMXR_MAXBUF - lp->rxbpi); + if (nbytes < 0) /* closed? reset ln */ + tmxr_reset_ln (lp); + else if (nbytes > 0) { /* if data rcvd */ + j = lp->rxbpi; /* start of data */ + memset (&lp->rbr[j], 0, nbytes); /* clear status */ + lp->rxbpi = lp->rxbpi + nbytes; /* adv pointers */ + lp->rxcnt = lp->rxcnt + nbytes; + +/* Examine new data, remove TELNET cruft before making input available */ + + for (; j < lp->rxbpi; ) { /* loop thru char */ + signed char tmp = lp->rxb[j]; /* get char */ + switch (lp->tsta) { /* case tlnt state */ + + case TNS_NORM: /* normal */ + if (tmp == TN_IAC) { /* IAC? */ + lp->tsta = TNS_IAC; /* change state */ + tmxr_rmvrc (lp, j); /* remove char */ + break; + } + if ((tmp == TN_CR) && lp->dstb) /* CR, no bin */ + lp->tsta = TNS_CRPAD; /* skip pad char */ + j = j + 1; /* advance j */ + break; + + case TNS_IAC: /* IAC prev */ + if ((tmp == TN_IAC) & !lp->dstb) { /* IAC + IAC, bin? */ + lp->tsta = TNS_NORM; /* treat as normal */ + j = j + 1; /* advance j */ + break; /* keep IAC */ + } + if (tmp == TN_BRK) { /* IAC + BRK? */ + lp->tsta = TNS_NORM; /* treat as normal */ + lp->rxb[j] = 0; /* char is null */ + lp->rbr[j] = 1; /* flag break */ + j = j + 1; /* advance j */ + break; + } + if (tmp == TN_WILL) /* IAC + WILL? */ + lp->tsta = TNS_WILL; + else if (tmp == TN_WONT) /* IAC + WONT? */ + lp->tsta = TNS_WONT; + else lp->tsta = TNS_SKIP; /* IAC + other */ + tmxr_rmvrc (lp, j); /* remove char */ + break; + + case TNS_WILL: case TNS_WONT: /* IAC+WILL/WONT prev */ + if (tmp == TN_BIN) { /* BIN? */ + if (lp->tsta == TNS_WILL) + lp->dstb = 0; + else lp->dstb = 1; + } + + /* Negotiation with the HP terminal emulator "QCTerm" is not working. + QCTerm says "WONT BIN" but sends bare CRs. RFC 854 says: + + Note that "CR LF" or "CR NUL" is required in both directions + (in the default ASCII mode), to preserve the symmetry of the + NVT model. ...The protocol requires that a NUL be inserted + following a CR not followed by a LF in the data stream. + + Until full negotiation is implemented, we work around the problem + by checking the character following the CR in non-BIN mode and + strip it only if it is LF or NUL. This should not affect + conforming clients. + */ + + case TNS_CRPAD: /* only LF or NUL should follow CR */ + lp->tsta = TNS_NORM; /* next normal */ + if ((tmp == TN_LF) || /* CR + LF ? */ + (tmp == TN_NUL)) /* CR + NUL? */ + tmxr_rmvrc (lp, j); /* remove it */ + break; + + case TNS_SKIP: default: /* skip char */ + lp->tsta = TNS_NORM; /* next normal */ + tmxr_rmvrc (lp, j); /* remove char */ + break; + } /* end case state */ + } /* end for char */ + } /* end else nbytes */ + } /* end for lines */ +for (i = 0; i < mp->lines; i++) { /* loop thru lines */ + lp = mp->ldsc + i; /* get line desc */ + if (lp->rxbpi == lp->rxbpr) /* if buf empty, */ + lp->rxbpi = lp->rxbpr = 0; /* reset pointers */ + } /* end for */ +return; +} + +/* Return count of available characters for line */ + +int32 tmxr_rqln (TMLN *lp) +{ +return (lp->rxbpi - lp->rxbpr); +} + +/* Remove character p (and matching status) from line l input buffer */ + +void tmxr_rmvrc (TMLN *lp, int32 p) +{ +for ( ; p < lp->rxbpi; p++) { + lp->rxb[p] = lp->rxb[p + 1]; + lp->rbr[p] = lp->rbr[p + 1]; + } +lp->rxbpi = lp->rxbpi - 1; +return; +} + +/* Store character in line buffer + + Inputs: + *lp = pointer to line descriptor + chr = characters + Outputs: + status = ok, connection lost, or stall +*/ + +t_stat tmxr_putc_ln (TMLN *lp, int32 chr) +{ +if (lp->txlog) /* log if available */ + fputc (chr, lp->txlog); +if (lp->conn == 0) /* no conn? lost */ + return SCPE_LOST; +if (tmxr_tqln (lp) < (TMXR_MAXBUF - 1)) { /* room for char (+ IAC)? */ + lp->txb[lp->txbpi] = (char) chr; /* buffer char */ + lp->txbpi = lp->txbpi + 1; /* adv pointer */ + if (lp->txbpi >= TMXR_MAXBUF) /* wrap? */ + lp->txbpi = 0; + if ((char) chr == TN_IAC) { /* IAC? */ + lp->txb[lp->txbpi] = (char) chr; /* IAC + IAC */ + lp->txbpi = lp->txbpi + 1; /* adv pointer */ + if (lp->txbpi >= TMXR_MAXBUF) /* wrap? */ + lp->txbpi = 0; + } + if (tmxr_tqln (lp) > (TMXR_MAXBUF - TMXR_GUARD)) /* near full? */ + lp->xmte = 0; /* disable line */ + return SCPE_OK; /* char sent */ + } +lp->xmte = 0; /* no room, dsbl line */ +return SCPE_STALL; /* char not sent */ +} + +/* Poll for output + + Inputs: + *mp = pointer to terminal multiplexor descriptor + Outputs: + none +*/ + +void tmxr_poll_tx (TMXR *mp) +{ +int32 i, nbytes; +TMLN *lp; + +for (i = 0; i < mp->lines; i++) { /* loop thru lines */ + lp = mp->ldsc + i; /* get line desc */ + if (lp->conn == 0) /* skip if !conn */ + continue; + nbytes = tmxr_send_buffered_data (lp); /* buffered bytes */ + if (nbytes == 0) /* buf empty? enab line */ + lp->xmte = 1; + } /* end for */ +return; +} + +/* Send buffered data across network + + Inputs: + *lp = pointer to line descriptor + Outputs: + returns number of bytes still buffered +*/ + +int32 tmxr_send_buffered_data (TMLN *lp) +{ +int32 nbytes, sbytes; + +nbytes = tmxr_tqln(lp); /* avail bytes */ +if (nbytes) { /* >0? write */ + if (lp->txbpr < lp->txbpi) /* no wrap? */ + sbytes = sim_write_sock (lp->conn, /* write all data */ + &(lp->txb[lp->txbpr]), nbytes); + else sbytes = sim_write_sock (lp->conn, /* write to end buf */ + &(lp->txb[lp->txbpr]), TMXR_MAXBUF - lp->txbpr); + if (sbytes != SOCKET_ERROR) { /* ok? */ + lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ + if (lp->txbpr >= TMXR_MAXBUF) /* wrap? */ + lp->txbpr = 0; + lp->txcnt = lp->txcnt + sbytes; /* update counts */ + nbytes = nbytes - sbytes; + } + if (nbytes && (lp->txbpr == 0)) { /* more data and wrap? */ + sbytes = sim_write_sock (lp->conn, lp->txb, nbytes); + if (sbytes != SOCKET_ERROR) { /* ok */ + lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ + if (lp->txbpr >= TMXR_MAXBUF) /* wrap? */ + lp->txbpr = 0; + lp->txcnt = lp->txcnt + sbytes; /* update counts */ + nbytes = nbytes - sbytes; + } + } + } /* end if nbytes */ +return nbytes; +} + +/* Return count of buffered characters for line */ + +int32 tmxr_tqln (TMLN *lp) +{ +return (lp->txbpi - lp->txbpr + ((lp->txbpi < lp->txbpr)? TMXR_MAXBUF: 0)); +} + +/* Open master socket */ + +t_stat tmxr_open_master (TMXR *mp, char *cptr) +{ +int32 i, port; +SOCKET sock; +TMLN *lp; +t_stat r; + +port = (int32) get_uint (cptr, 10, 65535, &r); /* get port */ +if ((r != SCPE_OK) || (port == 0)) + return SCPE_ARG; +sock = sim_master_sock (port); /* make master socket */ +if (sock == INVALID_SOCKET) /* open error */ + return SCPE_OPENERR; +printf ("Listening on port %d (socket %d)\n", port, sock); +if (sim_log) + fprintf (sim_log, "Listening on port %d (socket %d)\n", port, sock); +mp->port = port; /* save port */ +mp->master = sock; /* save master socket */ +for (i = 0; i < mp->lines; i++) { /* initialize lines */ + lp = mp->ldsc + i; + lp->conn = lp->tsta = 0; + lp->rxbpi = lp->rxbpr = 0; + lp->txbpi = lp->txbpr = 0; + lp->rxcnt = lp->txcnt = 0; + lp->xmte = 1; + lp->dstb = 0; + } +return SCPE_OK; +} + +/* Attach unit to master socket */ + +t_stat tmxr_attach (TMXR *mp, UNIT *uptr, char *cptr) +{ +char* tptr; +t_stat r; + +tptr = (char *) malloc (strlen (cptr) + 1); /* get string buf */ +if (tptr == NULL) /* no more mem? */ + return SCPE_MEM; +r = tmxr_open_master (mp, cptr); /* open master socket */ +if (r != SCPE_OK) { /* error? */ + free (tptr); /* release buf */ + return SCPE_OPENERR; + } +strcpy (tptr, cptr); /* copy port */ +uptr->filename = tptr; /* save */ +uptr->flags = uptr->flags | UNIT_ATT; /* no more errors */ + +if (mp->dptr == NULL) /* has device been set? */ + mp->dptr = find_dev_from_unit (uptr); /* no, so set device now */ + +return SCPE_OK; +} + +/* Close master socket */ + +t_stat tmxr_close_master (TMXR *mp) +{ +int32 i; +TMLN *lp; + +for (i = 0; i < mp->lines; i++) { /* loop thru conn */ + lp = mp->ldsc + i; + if (lp->conn) { + tmxr_linemsg (lp, "\r\nDisconnected from the "); + tmxr_linemsg (lp, sim_name); + tmxr_linemsg (lp, " simulator\r\n\n"); + tmxr_reset_ln (lp); + } /* end if conn */ + } /* end for */ +sim_close_sock (mp->master, 1); /* close master socket */ +mp->master = 0; +return SCPE_OK; +} + +/* Detach unit from master socket */ + +t_stat tmxr_detach (TMXR *mp, UNIT *uptr) +{ +if (!(uptr->flags & UNIT_ATT)) /* attached? */ + return SCPE_OK; +tmxr_close_master (mp); /* close master socket */ +free (uptr->filename); /* free port string */ +uptr->filename = NULL; +uptr->flags = uptr->flags & ~UNIT_ATT; /* not attached */ +return SCPE_OK; +} + +/* Stub examine and deposit */ + +t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +return SCPE_NOFNC; +} + +t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +return SCPE_NOFNC; +} + +/* Output message to socket or line descriptor */ + +void tmxr_msg (SOCKET sock, char *msg) +{ +if (sock) + sim_write_sock (sock, msg, strlen (msg)); +return; +} + +void tmxr_linemsg (TMLN *lp, char *msg) +{ +int32 len; + +for (len = strlen (msg); len > 0; --len) + tmxr_putc_ln (lp, *msg++); +return; +} + +/* Print connections - used only in named SHOW command */ + +void tmxr_fconns (FILE *st, TMLN *lp, int32 ln) +{ +if (ln >= 0) + fprintf (st, "line %d: ", ln); +if (lp->conn) { + int32 o1, o2, o3, o4, hr, mn, sc; + uint32 ctime; + + o1 = (lp->ipad >> 24) & 0xFF; + o2 = (lp->ipad >> 16) & 0xFF; + o3 = (lp->ipad >> 8) & 0xFF; + o4 = (lp->ipad) & 0xFF; + ctime = (sim_os_msec () - lp->cnms) / 1000; + hr = ctime / 3600; + mn = (ctime / 60) % 60; + sc = ctime % 60; + fprintf (st, "IP address %d.%d.%d.%d", o1, o2, o3, o4); + if (ctime) + fprintf (st, ", connected %02d:%02d:%02d\n", hr, mn, sc); + } +else fprintf (st, "line disconnected\n"); +if (lp->txlog) + fprintf (st, "Logging to %s\n", lp->txlogname); +return; +} + +/* Print statistics - used only in named SHOW command */ + +void tmxr_fstats (FILE *st, TMLN *lp, int32 ln) +{ +static const char *enab = "on"; +static const char *dsab = "off"; + +if (ln >= 0) + fprintf (st, "line %d: ", ln); +if (lp->conn) { + fprintf (st, "input (%s) queued/total = %d/%d, ", + (lp->rcve? enab: dsab), + lp->rxbpi - lp->rxbpr, lp->rxcnt); + fprintf (st, "output (%s) queued/total = %d/%d\n", + (lp->xmte? enab: dsab), + lp->txbpi - lp->txbpr, lp->txcnt); + } +else fprintf (st, "line disconnected\n"); +return; +} + +/* Disconnect line */ + +t_stat tmxr_dscln (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +TMXR *mp = (TMXR *) desc; +TMLN *lp; +int32 ln; +t_stat r; + +if (mp == NULL) + return SCPE_IERR; +if (val) { /* = n form */ + if (cptr == NULL) + return SCPE_ARG; + ln = (int32) get_uint (cptr, 10, mp->lines - 1, &r); + if (r != SCPE_OK) + return SCPE_ARG; + lp = mp->ldsc + ln; + } +else { + lp = tmxr_find_ldsc (uptr, 0, mp); + if (lp == NULL) + return SCPE_IERR; + } +if (lp->conn) { + tmxr_linemsg (lp, "\r\nOperator disconnected line\r\n\n"); + tmxr_reset_ln (lp); + } +return SCPE_OK; +} + +/* Enable logging for line */ + +t_stat tmxr_set_log (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +TMXR *mp = (TMXR *) desc; +TMLN *lp; + +if (cptr == NULL) /* no file name? */ + return SCPE_2FARG; +lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */ +if (lp == NULL) + return SCPE_IERR; +if (lp->txlog) /* close existing log */ + tmxr_set_nolog (NULL, val, NULL, desc); +lp->txlogname = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc namebuf */ +if (lp->txlogname == NULL) /* can't? */ + return SCPE_MEM; +strncpy (lp->txlogname, cptr, CBUFSIZE); /* save file name */ +lp->txlog = fopen (cptr, "ab"); /* open log */ +if (lp->txlog == NULL) { /* error? */ + free (lp->txlogname); /* free buffer */ + return SCPE_OPENERR; + } +return SCPE_OK; +} + +/* Disable logging for line */ + +t_stat tmxr_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +TMXR *mp = (TMXR *) desc; +TMLN *lp; + +if (cptr) /* no arguments */ + return SCPE_2MARG; +lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */ +if (lp == NULL) + return SCPE_IERR; +if (lp->txlog) { /* logging? */ + fclose (lp->txlog); /* close log */ + free (lp->txlogname); /* free namebuf */ + lp->txlog = NULL; + lp->txlogname = NULL; + } +return SCPE_OK; +} + +/* Show logging status for line */ + +t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +TMXR *mp = (TMXR *) desc; +TMLN *lp; + +lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */ +if (lp == NULL) + return SCPE_IERR; +if (lp->txlog) + fprintf (st, "logging to %s", lp->txlogname); +else fprintf (st, "no logging"); +return SCPE_OK; +} + +/* Find line descriptor. + + Note: This routine may be called with a UNIT that does not belong to the + device indicated in the TMXR structure. That is, the multiplexer lines may + belong to a device other than the one attached to the socket (the HP 2100 MUX + device is one example). Therefore, we must look up the device from the unit + at each call, rather than depending on the DPTR stored in the TMXR. +*/ + +TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, TMXR *mp) +{ +if (uptr) { /* called from SET? */ + DEVICE *dptr = find_dev_from_unit (uptr); /* find device */ + if (dptr == NULL) /* what?? */ + return NULL; + val = (int32) (uptr - dptr->units); /* implicit line # */ + } +if ((val < 0) || (val >= mp->lines)) /* invalid line? */ + return NULL; +return mp->ldsc + val; /* line descriptor */ +} + +/* Set the line connection order. + + Example command for eight-line multiplexer: + + SET LINEORDER=1;5;2-4;7 + + Resulting connection order: 1,5,2,3,4,7,0,6. + + Parameters: + - uptr = (not used) + - val = (not used) + - cptr = pointer to first character of range specification + - desc = pointer to multiplexer's TMXR structure + + On entry, cptr points to the value portion of the command string, which may + be either a semicolon-separated list of line ranges or the keyword ALL. + + If a line connection order array is not defined in the multiplexer + descriptor, the command is rejected. If the specified range encompasses all + of the lines, the first value of the connection order array is set to -1 to + indicate sequential connection order. Otherwise, the line values in the + array are set to the order specified by the command string. All values are + populated, first with those explicitly specified in the command string, and + then in ascending sequence with those not specified. + + If an error occurs, the original line order is not disturbed. +*/ + +t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +TMXR *mp = (TMXR *) desc; +char *tptr; +t_addr low, high, max = (t_addr) mp->lines - 1; +int32 *list; +t_bool *set; +uint32 line, idx = 0; +t_stat result = SCPE_OK; + +if (mp->lnorder == NULL) /* line connection order undefined? */ + return SCPE_NXPAR; /* "Non-existent parameter" error */ + +else if ((cptr == NULL) || (*cptr == '\0')) /* line range not supplied? */ + return SCPE_MISVAL; /* "Missing value" error */ + +list = (int32 *) calloc (mp->lines, sizeof (int32)); /* allocate new line order array */ + +if (list == NULL) /* allocation failed? */ + return SCPE_MEM; /* report it */ + +set = (t_bool *) calloc (mp->lines, sizeof (t_bool)); /* allocate line set tracking array */ + +if (set == NULL) { /* allocation failed? */ + free (list); /* free successful list allocation */ + return SCPE_MEM; /* report it */ + } + +tptr = cptr + strlen (cptr); /* append a semicolon */ +*tptr++ = ';'; /* to the command string */ +*tptr = '\0'; /* to make parsing easier for get_range */ + +while (*cptr) { /* parse command string */ + cptr = get_range (NULL, cptr, &low, &high, 10, max, ';'); /* get a line range */ + + if (cptr == NULL) { /* parsing error? */ + result = SCPE_ARG; /* "Invalid argument" error */ + break; + } + + else if ((low > max) || (high > max)) { /* line out of range? */ + result = SCPE_SUB; /* "Subscript out of range" error */ + break; + } + + else if ((low == 0) && (high == max)) { /* entire line range specified? */ + list [0] = -1; /* set sequential order flag */ + idx = (uint32) max + 1; /* indicate no fill-in needed */ + break; + } + + else + for (line = (uint32) low; line <= (uint32) high; line++) /* see if previously specified */ + if (set [line] == FALSE) { /* not already specified? */ + set [line] = TRUE; /* now it is */ + list [idx] = line; /* add line to connection order */ + idx = idx + 1; /* bump "specified" count */ + } + } + +if (result == SCPE_OK) { /* assignment successful? */ + if (idx <= max) /* any lines not specified? */ + for (line = 0; line <= max; line++) /* fill them in sequentially */ + if (set [line] == FALSE) { /* specified? */ + list [idx] = line; /* no, so add it */ + idx = idx + 1; + } + + memcpy (mp->lnorder, list, mp->lines * sizeof (int32)); /* copy working array to connection array */ + } + +free (list); /* free list allocation */ +free (set); /* free set allocation */ + +return result; +} + +/* Show line connection order. + + Parameters: + - st = stream on which output is to be written + - uptr = (not used) + - val = (not used) + - desc = pointer to multiplexer's TMXR structure + + If a connection order array is not defined in the multiplexer descriptor, the + command is rejected. If the first value of the connection order array is set + to -1, then the connection order is sequential. Otherwise, the line values + in the array are printed as a semicolon-separated list. Ranges are printed + where possible to shorten the output. +*/ + +t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, j, low, last; +TMXR *mp = (TMXR *) desc; +int32 *iptr = mp->lnorder; +t_bool first = TRUE; + +if (iptr == NULL) /* connection order undefined? */ + return SCPE_NXPAR; /* "Non-existent parameter" error */ + +if (*iptr < 0) /* sequential order indicated? */ + fprintf (st, "Order=0-%d\n", mp->lines - 1); /* print full line range */ + +else { + low = last = *iptr++; /* set first line value */ + + for (j = 1; j <= mp->lines; j++) { /* print remaining lines in order list */ + if (j < mp->lines) /* more lines to process? */ + i = *iptr++; /* get next line in list */ + else /* final iteration */ + i = -1; /* get "tie-off" value */ + + if (i != last + 1) { /* end of a range? */ + if (first) { /* first line to print? */ + fputs ("Order=", st); /* print header */ + first = FALSE; + } + + else /* not first line printed */ + fputc (';', st); /* print separator */ + + if (low == last) /* range null? */ + fprintf (st, "%d", last); /* print single line value */ + + else /* range established */ + fprintf (st, "%d-%d", low, last); /* print start and end line */ + + low = i; /* start new range */ + } + + last = i; /* note value for range check */ + } + } + +if (first == FALSE) /* sanity check for lines == 0 */ + fputc ('\n', st); + +return SCPE_OK; +} + +/* Show summary processor */ + +t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +TMXR *mp = (TMXR *) desc; +int32 i, t; + +if (mp == NULL) + return SCPE_IERR; +for (i = t = 0; i < mp->lines; i++) + t = t + (mp->ldsc[i].conn != 0); +if (t == 1) + fprintf (st, "1 connection"); +else fprintf (st, "%d connections", t); +return SCPE_OK; +} + +/* Show conn/stat processor */ + +t_stat tmxr_show_cstat (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +TMXR *mp = (TMXR *) desc; +int32 i, any; + +if (mp == NULL) + return SCPE_IERR; +for (i = any = 0; i < mp->lines; i++) { + if (mp->ldsc[i].conn) { + any++; + if (val) + tmxr_fconns (st, &mp->ldsc[i], i); + else tmxr_fstats (st, &mp->ldsc[i], i); + } + } +if (any == 0) + fprintf (st, (mp->lines == 1? "disconnected\n": "all disconnected\n")); +return SCPE_OK; +} + +/* Show number of lines */ + +t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +TMXR *mp = (TMXR *) desc; + +if (mp == NULL) + return SCPE_IERR; +fprintf (st, "lines=%d", mp->lines); +return SCPE_OK; +} +